Files
magic/windows/windClient.c
T
Patrick Kurth ea03883829 Fixed unused-parameters/unused-variables warnings
Added a ARG_UNUSED macro in utils/magic.h, which
produces a simple cast to void to silence the warnings.
The reasons this is used as opposed to an attribute is
that most compilers should support this.
Besides this, the macro can be easily searched for in
the future to potentially remove (where possible) the
unused parameters.
2025-10-10 22:12:19 +02:00

829 lines
22 KiB
C

/* windClient.c -
*
* Send button pushes and commands to the window's command
* interpreters.
*
* *********************************************************************
* * Copyright (C) 1985, 1990 Regents of the University of California. *
* * Permission to use, copy, modify, and distribute this *
* * software and its documentation for any purpose and without *
* * fee is hereby granted, provided that the above copyright *
* * notice appear in all copies. The University of California *
* * makes no representations about the suitability of this *
* * software for any purpose. It is provided "as is" without *
* * express or implied warranty. Export of this software outside *
* * of the United States of America may require an export license. *
* *********************************************************************
*/
#ifndef lint
static char rcsid[] __attribute__ ((unused)) ="$Header: /usr/cvsroot/magic-8.0/windows/windClient.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include "utils/magic.h"
#include "textio/textio.h"
#include "utils/geometry.h"
#include "windows/windows.h"
#include "graphics/glyphs.h"
#include "windows/windInt.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "utils/main.h"
#include "utils/macros.h"
#include "utils/utils.h"
#include "utils/malloc.h"
#include "graphics/graphics.h"
#include "utils/styles.h"
#include "textio/txcommands.h"
#include "utils/undo.h"
/* The following defines are used to indicate corner positions
* of the box:
*/
#define WIND_BL 0
#define WIND_BR 1
#define WIND_TR 2
#define WIND_TL 3
#define WIND_ILG -1
/* our window client ID */
global WindClient windClientID = (WindClient) NULL;
extern int windBorderCmd();
extern int windCaptionCmd(), windCrashCmd(), windCursorCmd();
extern int windFilesCmd(), windCloseCmd(), windOpenCmd();
extern int windQuitCmd(), windRedrawCmd();
extern int windResetCmd(), windSpecialOpenCmd();
extern int windOverCmd(), windUnderCmd(), windDebugCmd();
extern int windDumpCmd(), windHelpCmd();
extern int windMacroCmd(), windIntMacroCmd();
extern int windLogCommandsCmd(), windUpdateCmd(), windSleepCmd();
extern int windSetpointCmd();
extern int windPushbuttonCmd();
extern int windPauseCmd(), windGrstatsCmd();
extern int windGrowCmd();
extern int windUndoCmd(), windRedoCmd();
extern int windCenterCmd(), windScrollCmd();
extern int windVersionCmd(), windViewCmd(), windXviewCmd(), windZoomCmd();
extern int windScrollBarsCmd(), windPositionsCmd();
extern int windNamesCmd();
#ifdef MAGIC_WRAPPER
extern int windBypassCmd();
#else
extern int windEchoCmd(), windSourceCmd(), windSendCmd();
#endif
static Rect windFrameRect;
static MagWindow *windFrameWindow;
static int windButton = TX_LEFT_BUTTON;
static int windCorner = WIND_ILG; /* Nearest corner when button went
* down.
*/
/*
* ----------------------------------------------------------------------------
* windButtonSetCursor --
*
* Used to set the programmable cursor for a particular
* button state.
*
* Results:
* None.
*
* Side effects:
* Selects and sets a programmable cursor based on the given
* button (for sizing or moving) and corner.
* ----------------------------------------------------------------------------
*/
void
windButtonSetCursor(button, corner)
int button; /* Button that is down. */
int corner; /* Corner to be displayed in cursor. */
{
switch (corner)
{
case WIND_BL:
if (button == TX_LEFT_BUTTON)
GrSetCursor(STYLE_CURS_LLWIND);
else
GrSetCursor(STYLE_CURS_LLWINDCORN);
break;
case WIND_BR:
if (button == TX_LEFT_BUTTON)
GrSetCursor(STYLE_CURS_LRWIND);
else
GrSetCursor(STYLE_CURS_LRWINDCORN);
break;
case WIND_TL:
if (button == TX_LEFT_BUTTON)
GrSetCursor(STYLE_CURS_ULWIND);
else
GrSetCursor(STYLE_CURS_ULWINDCORN);
break;
case WIND_TR:
if (button == TX_LEFT_BUTTON)
GrSetCursor(STYLE_CURS_URWIND);
else
GrSetCursor(STYLE_CURS_URWINDCORN);
break;
}
}
/*
* ----------------------------------------------------------------------------
* windGetCorner --
*
* Returns the corner of the window closest to a given screen location.
*
* Results:
* An integer value is returned, indicating the corner closest to
* the given screen location.
*
* Side effects:
* None.
* ----------------------------------------------------------------------------
*/
int
windGetCorner(screenPoint, screenRect)
Point *screenPoint;
Rect *screenRect;
{
Rect r;
/* Find out which corner is closest. Consider only the
* intersection of the box with the window (otherwise it
* may not be possible to select off-screen corners.
*/
r = *screenRect;
GeoClip(&r, &GrScreenRect);
if (screenPoint->p_x < ((r.r_xbot + r.r_xtop)/2))
{
if (screenPoint->p_y < ((r.r_ybot + r.r_ytop)/2))
return WIND_BL;
else
return WIND_TL;
}
else
{
if (screenPoint->p_y < ((r.r_ybot + r.r_ytop)/2))
return WIND_BR;
else
return WIND_TR;
}
}
/*
* ----------------------------------------------------------------------------
* windMoveRect --
*
* Repositions a rectangle by one of its corners.
*
* Results:
* None.
*
* Side effects:
* The rectangle is changed so that the given corner is at the
* given position.
* ----------------------------------------------------------------------------
*/
void
windMoveRect(wholeRect, corner, p, rect)
bool wholeRect; /* move the whole thing? or just a corner? */
int corner; /* Specifies a corner in the format
* returned by ToolGetCorner.
*/
Point *p; /* New position of corner, in screen
* coordinates.
*/
Rect *rect;
{
int x, y, tmp;
/* Move the rect. If an illegal corner is specified, then
* move by the bottom-left corner.
*/
if (wholeRect)
{
switch (corner)
{
case WIND_BL:
x = p->p_x - rect->r_xbot;
y = p->p_y - rect->r_ybot;
break;
case WIND_BR:
x = p->p_x - rect->r_xtop;
y = p->p_y - rect->r_ybot;
break;
case WIND_TR:
x = p->p_x - rect->r_xtop;
y = p->p_y - rect->r_ytop;
break;
case WIND_TL:
x = p->p_x - rect->r_xbot;
y = p->p_y - rect->r_ytop;
break;
default:
x = p->p_x - rect->r_xbot;
y = p->p_y - rect->r_ybot;
break;
}
rect->r_xbot += x;
rect->r_ybot += y;
rect->r_xtop += x;
rect->r_ytop += y;
}
else
{
switch (corner)
{
case WIND_BL:
rect->r_xbot = p->p_x;
rect->r_ybot = p->p_y;
break;
case WIND_BR:
rect->r_xtop = p->p_x;
rect->r_ybot = p->p_y;
break;
case WIND_TR:
rect->r_xtop = p->p_x;
rect->r_ytop = p->p_y;
break;
case WIND_TL:
rect->r_xbot = p->p_x;
rect->r_ytop = p->p_y;
break;
}
/* If the movement turned the box inside out, turn it right
* side out again.
*/
if (rect->r_xbot > rect->r_xtop)
{
tmp = rect->r_xtop;
rect->r_xtop = rect->r_xbot;
rect->r_xbot = tmp;
}
if (rect->r_ybot > rect->r_ytop)
{
tmp = rect->r_ytop;
rect->r_ytop = rect->r_ybot;
rect->r_ybot = tmp;
}
}
}
/*
* ----------------------------------------------------------------------------
* Button Routines --
*
* This page contains a set of routines to handle the puck
* buttons within the window border.
*
* Results:
* None.
*
* Side effects:
* Left button: used to move the whole window by the lower-left corner.
* Right button: used to re-size the window by its upper-right corner.
* If one of the left or right buttons is pushed, then the
* other is pushed, the corner is switched to the nearest
* one to the cursor. This corner is remembered for use
* in box positioning/sizing when both buttons have gone up.
* Bottom button: Center the view on the point where the crosshair is
* at when the button is released.
* ----------------------------------------------------------------------------
*/
void
windFrameDown(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
if (WindOldButtons == 0)
{
windFrameRect = w->w_frameArea;
windFrameWindow = w;
windButton = cmd->tx_button;
}
#define BOTHBUTTONS (TX_LEFT_BUTTON | TX_RIGHT_BUTTON)
if ((WindNewButtons & BOTHBUTTONS) == BOTHBUTTONS)
{
windCorner = windGetCorner(&(cmd->tx_p), &(windFrameWindow->w_frameArea));
}
else if (cmd->tx_button == TX_LEFT_BUTTON)
{
windCorner = WIND_BL;
windButtonSetCursor(windButton, windCorner);
}
else if (cmd->tx_button == TX_RIGHT_BUTTON)
{
windCorner = WIND_TR;
windButtonSetCursor(windButton, windCorner);
}
}
/*ARGSUSED*/
void
windFrameUp(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
ARG_UNUSED(w);
if (WindNewButtons == 0)
{
GrSetCursor(STYLE_CURS_NORMAL);
switch (cmd->tx_button)
{
case TX_LEFT_BUTTON:
case TX_RIGHT_BUTTON:
windMoveRect( (windButton == TX_LEFT_BUTTON),
windCorner, &(cmd->tx_p), &windFrameRect);
WindReframe(windFrameWindow, &windFrameRect, FALSE,
(windButton == TX_LEFT_BUTTON) );
break;
}
}
else
{
/* If both buttons are down and one is released, we just change
* the cursor to reflect the current corner and the remaining
* button (i.e. move or size window).
*/
windCorner = windGetCorner(&(cmd->tx_p), &(windFrameWindow->w_frameArea));
windButtonSetCursor(windButton, windCorner);
}
}
/*
* ----------------------------------------------------------------------------
* windFrameButtons --
*
* Handle button pushes to the window frame area (zoom and scroll)
* Always handle scroll bars ourselves, even if there is an external
* window package. BUT---in the Tcl/Tk version of Magic, the
* window does not set WIND_SCROLLBARS, causing this routine to be
* bypassed.
*
* Results:
* TRUE if the button was pushed in the frame and handled by one
* of the button handler routines, FALSE if not.
*
* Side effects:
* Depends upon where the button was pushed.
* ----------------------------------------------------------------------------
*/
bool
windFrameButtons(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
extern void windBarLocations();
Rect leftBar, botBar, up, down, right, left, zoom;
Point p;
if (w == NULL) return FALSE;
p.p_x = w->w_screenArea.r_xtop - w->w_screenArea.r_xbot;
p.p_y = w->w_screenArea.r_ytop - w->w_screenArea.r_ybot;
if ((w->w_flags & WIND_SCROLLBARS) != 0)
{
windBarLocations(w, &leftBar, &botBar, &up, &down, &right, &left, &zoom);
if (cmd->tx_button == TX_MIDDLE_BUTTON) {
if (GEO_ENCLOSE(&cmd->tx_p, &leftBar))
{
/* move elevator */
p.p_x = 0;
p.p_y = w->w_bbox->r_ybot +
((w->w_bbox->r_ytop - w->w_bbox->r_ybot) *
(cmd->tx_p.p_y - leftBar.r_ybot))
/ (leftBar.r_ytop - leftBar.r_ybot) -
(w->w_surfaceArea.r_ytop + w->w_surfaceArea.r_ybot)/2;
WindScroll(w, &p, (Point *) NULL);
return TRUE;
}
else if (GEO_ENCLOSE(&cmd->tx_p, &botBar))
{
/* move elevator */
p.p_y = 0;
p.p_x = w->w_bbox->r_xbot +
((w->w_bbox->r_xtop - w->w_bbox->r_xbot) *
(cmd->tx_p.p_x - botBar.r_xbot))
/ (botBar.r_xtop - botBar.r_xbot) -
(w->w_surfaceArea.r_xtop + w->w_surfaceArea.r_xbot)/2;
WindScroll(w, &p, (Point *) NULL);
return TRUE;
}
else if (GEO_ENCLOSE(&cmd->tx_p, &up))
{
/* scroll up */
p.p_y = -p.p_y;
p.p_x = 0;
WindScroll(w, (Point *) NULL, &p);
return TRUE;
}
else if (GEO_ENCLOSE(&cmd->tx_p, &down))
{
/* scroll down */
p.p_x = 0;
WindScroll(w, (Point *) NULL, &p);
return TRUE;
}
else if (GEO_ENCLOSE(&cmd->tx_p, &right))
{
/* scroll right */
p.p_x = -p.p_x;
p.p_y = 0;
WindScroll(w, (Point *) NULL, &p);
return TRUE;
}
else if (GEO_ENCLOSE(&cmd->tx_p, &left))
{
/* scroll left */
p.p_y = 0;
WindScroll(w, (Point *) NULL, &p);
return TRUE;
}
}
if (GEO_ENCLOSE(&cmd->tx_p, &zoom)) {
/* zoom in, out, or view */
switch (cmd->tx_button)
{
case TX_LEFT_BUTTON:
WindZoom(w, 2.0);
break;
case TX_MIDDLE_BUTTON:
WindView(w);
break;
case TX_RIGHT_BUTTON:
WindZoom(w, 0.5);
break;
}
return TRUE;
}
}
return FALSE;
}
/*
* ----------------------------------------------------------------------------
* windClientButtons --
*
* Handle button pushes to the window border.
*
* Results:
* None.
*
* Side effects:
* depends upon where the button was pushed.
* ----------------------------------------------------------------------------
*/
void
windClientButtons(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
/*
* Is this an initial 'down push' in a non-iconic window? If so, we
* will initiate some user-interaction sequence, such as moving the corner
* of the window or growing it to full-screen size.
*
* (An 'iconic' window is one that is closed down to an icon -- this
* currently only happens when we are using the Sun window package, but
* in the future it might happen in other cases too.)
*
*/
if ((WindOldButtons == 0) && ((w->w_flags & WIND_ISICONIC) == 0))
{
/* single button down */
Point p;
Rect caption;
windFrameWindow = NULL;
if (w == NULL) return;
p.p_x = w->w_screenArea.r_xtop - w->w_screenArea.r_xbot;
p.p_y = w->w_screenArea.r_ytop - w->w_screenArea.r_ybot;
caption = w->w_allArea;
if ((w->w_flags & WIND_CAPTION) != 0)
caption.r_ybot = caption.r_ytop - TOP_BORDER(w) + 1;
else
caption.r_ybot = caption.r_ytop;
/* Handle 'grow' for our window package. */
if (WindPackageType == WIND_MAGIC_WINDOWS)
{
if ((cmd->tx_button == TX_MIDDLE_BUTTON) &&
GEO_ENCLOSE(&cmd->tx_p, &caption))
{
WindFullScreen(w);
return;
}
}
if (windFrameButtons(w, cmd)) return;
/* Otherwise, continue onward */
}
/*
* At this point, we have decided that the button was not an initial
* button push for Magic's window package. Maybe an external window
* package wants it, or maybe it is a continuation of a previous Magic
* sequence (such as moving a corner of a window).
*/
switch ( WindPackageType )
{
case WIND_X_WINDOWS:
break;
default:
/* Magic Windows */
if (cmd->tx_button == TX_MIDDLE_BUTTON)
return;
if ((cmd->tx_buttonAction == TX_BUTTON_UP) &&
(windFrameWindow == NULL))
return;
/* no special area or else an up push -- reframe window */
switch (cmd->tx_buttonAction)
{
case TX_BUTTON_DOWN:
windFrameDown(w, cmd);
break;
case TX_BUTTON_UP:
windFrameUp(w, cmd);
break;
default:
TxError("windClientButtons() failed!\n");
break;
}
}
}
/*
* ----------------------------------------------------------------------------
* WindButtonInFrame --
*
* To be called from the graphics packages. Handle button pushes to
* the window frame area (zoom and scroll) by calling windFrameButtons()
* if it appears to be appropriate for this MagWindow structure.
* This bypasses the key macro handler, thus hard-coding the button
* actions for the frame.
*
* Results:
* TRUE if the button was pushed in the frame and handled by one
* of the button handler routines, FALSE if not.
*
* Side effects:
* Depends upon where the button was pushed.
* ----------------------------------------------------------------------------
*/
bool
WindButtonInFrame(w, x, y, b)
MagWindow *w;
int x;
int y;
int b;
{
TxCommand cmd;
cmd.tx_p.p_x = x;
cmd.tx_p.p_y = y;
cmd.tx_button = b;
if (windFrameButtons(w, &cmd))
{
WindUpdate();
return TRUE;
}
return FALSE;
}
/*
* ----------------------------------------------------------------------------
*
* windClientInterp
*
* Window's command interpreter.
* Dispatches to long commands, providing them with the window and command
* we are passed.
*
* Results:
* none.
*
* Side effects:
* Whatever occur as a result of executing the long
* command supplied.
*
* ----------------------------------------------------------------------------
*/
void
windCmdInterp(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
switch (cmd->tx_button)
{
case TX_LEFT_BUTTON:
case TX_RIGHT_BUTTON:
case TX_MIDDLE_BUTTON:
windClientButtons(w, cmd);
break;
case TX_NO_BUTTON:
if (WindExecute(w, windClientID, cmd) >= 0)
UndoNext();
break;
default:
ASSERT(FALSE, "windCmdInterp");
break;
}
}
/*
* ----------------------------------------------------------------------------
* windCmdInit --
*
* Initialize the window client.
*
* Results:
* None.
*
* Side effects:
* None.
* ----------------------------------------------------------------------------
*/
void
windClientInit()
{
windClientID = WindAddClient(WINDOW_CLIENT, ( bool (*)() ) NULL,
( bool (*)() ) NULL, ( void (*)() ) NULL, windCmdInterp,
( void (*)() ) NULL, ( bool (*)() ) NULL, ( void (*)() ) NULL,
(GrGlyph *) NULL);
/* Set up all of the window commands */
#ifdef MAGIC_WRAPPER
WindAddCommand(windClientID,
"*bypass command run command independently of the command line",
windBypassCmd, FALSE);
#endif
WindAddCommand(windClientID,
"*crash cause a core dump",
windCrashCmd, FALSE);
WindAddCommand(windClientID,
"*files print out currently open files",
windFilesCmd, FALSE);
WindAddCommand(windClientID,
"*grstats print out stats on graphics",
windGrstatsCmd, FALSE);
WindAddCommand(windClientID,
"*pause [args] print args and wait for <cr>",
windPauseCmd, FALSE);
WindAddCommand(windClientID,
"*winddebug set debugging mode",
windDebugCmd, FALSE);
WindAddCommand(windClientID,
"*winddump print out debugging info",
windDumpCmd, FALSE);
WindAddCommand(windClientID,
"center [x y] center window on the cursor or indicated coordinate",
windCenterCmd, FALSE);
WindAddCommand(windClientID,
"closewindow [name] close a window",
windCloseCmd, FALSE);
WindAddCommand(windClientID,
"cursor return magic coordinates of the cursor",
windCursorCmd, FALSE);
WindAddCommand(windClientID,
"grow blow a window up to full-screen size or back again",
windGrowCmd, FALSE);
WindAddCommand(windClientID,
"help [pattern] print out synopses for all commands valid\n\
in the current window (or just those\n\
containing pattern)",
windHelpCmd, FALSE);
WindAddCommand(windClientID,
"imacro [char [string]] define or print an interactive macro called char",
windIntMacroCmd, FALSE);
WindAddCommand(windClientID,
"logcommands [file [update]]\n\
log all commands into a file",
windLogCommandsCmd, FALSE);
WindAddCommand(windClientID,
"macro [char [string]] define or print a macro called char",
windMacroCmd, FALSE);
WindAddCommand(windClientID,
"openwindow [cell][name]\n\
open a new window with indicated name, bound to indicated cell",
windOpenCmd, FALSE);
WindAddCommand(windClientID,
"over move a window over (on top of) the rest",
windOverCmd, FALSE);
WindAddCommand(windClientID,
"pushbutton button act push a mouse button",
windPushbuttonCmd, FALSE);
WindAddCommand(windClientID,
"redo [count] redo commands",
windRedoCmd, FALSE);
WindAddCommand(windClientID,
"redraw redraw the display",
windRedrawCmd, FALSE);
WindAddCommand(windClientID,
"reset reset the display",
windResetCmd, FALSE);
WindAddCommand(windClientID,
"scroll dir [amount] scroll the window",
windScrollCmd, FALSE);
WindAddCommand(windClientID,
"setpoint [x y [WID]] force to cursor (point) to x,y in window WID",
windSetpointCmd, FALSE);
WindAddCommand(windClientID,
"sleep seconds sleep for a number of seconds",
windSleepCmd, FALSE);
WindAddCommand(windClientID,
"specialopen [coords] type [args]\n\
open a special window",
windSpecialOpenCmd, FALSE);
WindAddCommand(windClientID,
"quit exit magic",
windQuitCmd, FALSE);
WindAddCommand(windClientID,
"underneath move a window underneath the rest",
windUnderCmd, FALSE);
WindAddCommand(windClientID,
"undo [count] undo commands",
windUndoCmd, FALSE);
WindAddCommand(windClientID,
#ifdef MAGIC_WRAPPER
"updatedisplay [suspend|resume]\n\
force display update, or suspend/resume updates",
#else
"updatedisplay force the display to be updated",
#endif
windUpdateCmd, FALSE);
WindAddCommand(windClientID,
"version print out version info",
windVersionCmd, FALSE);
WindAddCommand(windClientID,
"view [get] zoom window out so everything is visible",
windViewCmd, FALSE);
WindAddCommand(windClientID,
"windowborder [on|off] toggle border drawing for new windows",
windBorderCmd, FALSE);
WindAddCommand(windClientID,
"windowcaption [on|off] toggle title caption for new windows",
windCaptionCmd, FALSE);
WindAddCommand(windClientID,
"windowscrollbars [on|off]\n\
toggle scroll bars for new windows",
windScrollBarsCmd, FALSE);
WindAddCommand(windClientID,
"windowpositions [file] print out window positions",
windPositionsCmd, FALSE);
WindAddCommand(windClientID,
"xview zoom window out so everything is unexpanded",
windXviewCmd, FALSE);
WindAddCommand(windClientID,
"zoom amount zoom window by amount",
windZoomCmd, FALSE);
WindAddCommand(windClientID,
"windownames [all|type] get name of current or all windows",
windNamesCmd, FALSE);
#ifndef MAGIC_WRAPPER
WindAddCommand(windClientID,
"echo [-n] [strings] print text on the terminal",
windEchoCmd, FALSE);
WindAddCommand(windClientID,
"send type command send a command to a certain window type",
windSendCmd, FALSE);
WindAddCommand(windClientID,
"source filename read in commands from file",
windSourceCmd, FALSE);
#endif
}