📄 srvevent.c
字号:
/*
* Copyright (c) 2000 Greg Haerr <greg@censoft.com>
* Copyright (c) 1991 David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
* Graphics server event routines for windows.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "serv.h"
#ifndef __ECOS
/* readable error strings*/
char *nxErrorStrings[] = {
GR_ERROR_STRINGS
};
#endif
#if NONETWORK
/* [copied from client.c]
* The following is the user defined function for handling errors.
* If this is not set, then the default action is to close the connection
* to the server, describe the error, and then exit. This error function
* will only be called when the client asks for events.
*/
static GR_FNCALLBACKEVENT ErrorFunc = GrDefaultErrorHandler;
/*
* The default error handler which is called when the server
* reports an error event and the client hasn't set a handler for error events.
*/
void
GrDefaultErrorHandler(GR_EVENT *ep)
{
if (ep->type == GR_EVENT_TYPE_ERROR) {
EPRINTF("nxclient: Error (%s) ", ep->error.name);
EPRINTF(nxErrorStrings[ep->error.code], ep->error.id);
GrClose();
exit(1);
}
}
/*
* Set an error handling routine, which will be called on any errors from
* the server (when events are asked for by the client). If zero is given,
* then errors will be returned as regular events.
* Returns the previous error handler.
*/
GR_FNCALLBACKEVENT
GrSetErrorHandler(GR_FNCALLBACKEVENT fncb)
{
GR_FNCALLBACKEVENT orig = ErrorFunc;
ErrorFunc = fncb;
return orig;
}
#endif /* NONETWORK*/
/*
* Generate an error from a graphics function.
* This creates a special event which describes the error.
* Only one error event at a time can be saved for delivery to a client.
* This is ok since there are usually lots of redundant errors generated
* before the client can notice, errors occurs after the fact, and clients
* can't do much about them except complain and die. The error is saved
* specially so that memory problems cannot occur.
*/
void GsError(GR_ERROR code, GR_ID id)
{
GR_EVENT_ERROR *ep; /* event to describe error */
EPRINTF("nano-X: GsError ");
if(curfunc)
EPRINTF("(%s) ", curfunc);
EPRINTF(nxErrorStrings[code], id);
/* if no clients, nothing to report*/
if (!curclient)
return;
/* queue the error event regardless of GrSelectEvents*/
ep = (GR_EVENT_ERROR *)GsAllocEvent(curclient);
ep->type = GR_EVENT_TYPE_ERROR;
ep->name[0] = 0;
if(curfunc) {
strncpy(ep->name, curfunc, sizeof(GR_FUNC_NAME));
ep->name[sizeof(GR_FUNC_NAME)-1] = '\0';
}
ep->code = code;
ep->id = id;
}
/*
* Allocate an event to be passed back to the specified client.
* The event is already chained onto the event queue, and only
* needs filling out. Returns NULL with an error generated if
* the event cannot be allocated.
*/
GR_EVENT *GsAllocEvent(GR_CLIENT *client)
{
GR_EVENT_LIST *elp; /* current element list */
GR_CLIENT *oldcurclient; /* old current client */
/*
* Get a new event structure from the free list, or else
* allocate it using malloc.
*/
elp = eventfree;
if (elp)
eventfree = elp->next;
else {
elp = (GR_EVENT_LIST *) malloc(sizeof(GR_EVENT_LIST));
if (elp == NULL) {
oldcurclient = curclient;
curclient = client;
GsError(GR_ERROR_MALLOC_FAILED, 0);
curclient = oldcurclient;
return NULL;
}
}
/*
* Add the event to the end of the event list.
*/
if (client->eventhead)
client->eventtail->next = elp;
else
client->eventhead = elp;
client->eventtail = elp;
elp->next = NULL;
elp->event.type = GR_EVENT_TYPE_NONE;
return &elp->event;
}
/*
* Update mouse status and issue events on it if necessary.
* This function doesn't block, but is normally only called when
* there is known to be some data waiting to be read from the mouse.
*/
GR_BOOL GsCheckMouseEvent(void)
{
GR_COORD rootx; /* latest mouse x position */
GR_COORD rooty; /* latest mouse y position */
int newbuttons; /* latest buttons */
int mousestatus; /* latest mouse status */
/* Read the latest mouse status: */
mousestatus = GdReadMouse(&rootx, &rooty, &newbuttons);
if(mousestatus < 0) {
GsError(GR_ERROR_MOUSE_ERROR, 0);
return FALSE;
} else if(mousestatus) { /* Deliver events as appropriate: */
GsHandleMouseStatus(rootx, rooty, newbuttons);
/* possibly reset portrait mode based on mouse position*/
if (autoportrait)
GsSetPortraitModeFromXY(rootx, rooty);
return TRUE;
}
return FALSE;
}
/*
* Update keyboard status and issue events on it if necessary.
* This function doesn't block, but is normally only called when
* there is known to be some data waiting to be read from the keyboard.
*/
GR_BOOL GsCheckKeyboardEvent(void)
{
MWKEY mwkey; /* latest character */
MWKEYMOD modifiers; /* latest modifiers */
MWSCANCODE scancode;
int keystatus; /* latest keyboard status */
/* Read the latest keyboard status: */
keystatus = GdReadKeyboard(&mwkey, &modifiers, &scancode);
if(keystatus < 0) {
if(keystatus == -2) /* special case return code*/
GsTerminate();
GsError(GR_ERROR_KEYBOARD_ERROR, 0);
return FALSE;
} else if(keystatus) { /* Deliver events as appropriate: */
switch (mwkey) {
case MWKEY_QUIT:
GsTerminate();
/* no return*/
case MWKEY_REDRAW:
GsRedrawScreen();
break;
case MWKEY_PRINT:
if (keystatus == 1)
GdCaptureScreen("screen.bmp");
break;
}
GsDeliverKeyboardEvent(0,
(keystatus==1?
GR_EVENT_TYPE_KEY_DOWN: GR_EVENT_TYPE_KEY_UP),
mwkey, modifiers, scancode);
return TRUE;
}
return FALSE;
}
/*
* Handle all mouse events. These are mouse enter, mouse exit, mouse
* motion, mouse position, button down, and button up. This also moves
* the cursor to the new mouse position and changes it shape if needed.
*/
void GsHandleMouseStatus(GR_COORD newx, GR_COORD newy, int newbuttons)
{
int changebuttons; /* buttons that have changed */
MWKEYMOD modifiers; /* latest modifiers */
GdGetModifierInfo(NULL, &modifiers); /* Read kbd modifiers */
/*
* First, if the mouse has moved, then position the cursor to the
* new location, which will send mouse enter, mouse exit, focus in,
* and focus out events if needed. Check here for mouse motion and
* mouse position events. Flush the device queue to make sure the
* new cursor location is quickly seen by the user.
*/
if ((newx != cursorx) || (newy != cursory)) {
GsResetScreenSaver();
GrMoveCursor(newx, newy);
GsDeliverMotionEvent(GR_EVENT_TYPE_MOUSE_MOTION,
newbuttons, modifiers);
GsDeliverMotionEvent(GR_EVENT_TYPE_MOUSE_POSITION,
newbuttons, modifiers);
}
/*
* Next, generate a button up event if any buttons have been released.
*/
changebuttons = (curbuttons & ~newbuttons);
if (changebuttons) {
GsResetScreenSaver();
GsDeliverButtonEvent(GR_EVENT_TYPE_BUTTON_UP,
newbuttons, changebuttons, modifiers);
}
/*
* Finally, generate a button down event if any buttons have been
* pressed.
*/
changebuttons = (~curbuttons & newbuttons);
if (changebuttons) {
GsResetScreenSaver();
GsDeliverButtonEvent(GR_EVENT_TYPE_BUTTON_DOWN,
newbuttons, changebuttons, modifiers);
}
curbuttons = newbuttons;
}
/*
* Deliver a mouse button event to the clients which have selected for it.
* Each client can only be delivered one instance of the event. The window
* the event is delivered for is either the smallest one containing the
* mouse coordinates, or else one of its direct ancestors. The lowest
* window in that tree which has enabled for the event gets it. This scan
* is done independently for each client. If a window with the correct
* noprop mask is reached, or if no window selects for the event, then the
* event is discarded for that client. Special case: for the first client
* that is enabled for both button down and button up events in a window,
* then the pointer is implicitly grabbed by that window when a button is
* pressed down in that window. The grabbing remains until all buttons are
* released. While the pointer is grabbed, no other clients or windows can
* receive button down or up events.
*/
void GsDeliverButtonEvent(GR_EVENT_TYPE type, int buttons, int changebuttons,
int modifiers)
{
GR_EVENT_BUTTON *ep; /* mouse button event */
GR_WINDOW *wp; /* current window */
GR_EVENT_CLIENT *ecp; /* current event client */
GR_CLIENT *client; /* current client */
GR_WINDOW_ID subwid; /* subwindow id event is for */
GR_EVENT_MASK eventmask; /* event mask */
GR_EVENT_MASK tempmask; /* to get around compiler bug */
eventmask = GR_EVENTMASK(type);
if (eventmask == 0)
return;
/*
* If the pointer is implicitly grabbed, then the only window
* which can receive button events is that window. Otherwise
* the window the pointer is in gets the events. Determine the
* subwindow by seeing if it is a child of the grabbed button.
*/
wp = mousewp;
subwid = wp->id;
if (grabbuttonwp) {
while ((wp != rootwp) && (wp != grabbuttonwp))
wp = wp->parent;
#if 0
if (wp != grabbuttonwp)
subwid = grabbuttonwp->id;
#endif
wp = grabbuttonwp;
}
for (;;) {
for (ecp = wp->eventclients; ecp; ecp = ecp->next) {
if ((ecp->eventmask & eventmask) == 0)
continue;
client = ecp->client;
/*
* If this is a button down, the buttons are not
* yet grabbed, and this client is enabled for both
* button down and button up events, then implicitly
* grab the window for him.
*/
if ((type == GR_EVENT_TYPE_BUTTON_DOWN)
&& (grabbuttonwp == NULL))
{
tempmask = GR_EVENT_MASK_BUTTON_UP;
if (ecp->eventmask & tempmask) {
/*DPRINTF("nano-X: implicit grab on window %d\n", wp->id);*/
grabbuttonwp = wp;
}
}
ep = (GR_EVENT_BUTTON *) GsAllocEvent(client);
if (ep == NULL)
continue;
ep->type = type;
ep->wid = wp->id;
ep->subwid = subwid;
ep->rootx = cursorx;
ep->rooty = cursory;
ep->x = cursorx - wp->x;
ep->y = cursory - wp->y;
ep->buttons = buttons;
ep->changebuttons = changebuttons;
ep->modifiers = modifiers;
ep->time = GsGetTickCount();
}
/*
* Events do not propagate if the window was grabbed.
* Also release the grab if the buttons are now all released,
* which can cause various events.
*/
if (grabbuttonwp) {
if (buttons == 0) {
grabbuttonwp = NULL;
GrMoveCursor(cursorx, cursory);
}
return;
}
if ((wp == rootwp) || (wp->nopropmask & eventmask))
return;
wp = wp->parent;
}
}
/*
* Deliver a mouse motion event to the clients which have selected for it.
* Each client can only be delivered one instance of the event. The window
* the event is delivered for is either the smallest one containing the
* mouse coordinates, or else one of its direct ancestors. The lowest
* window in that tree which has enabled for the event gets it. This scan
* is done independently for each client. If a window with the correct
* noprop mask is reached, or if no window selects for the event, then the
* event is discarded for that client. Special case: If the event type is
* GR_EVENT_TYPE_MOUSE_POSITION, then only the last such event is queued for
* any single client to reduce events. If the mouse is implicitly grabbed,
* then only the grabbing window receives the events, and continues to do
* so even if the mouse is currently outside of the grabbing window.
*/
void GsDeliverMotionEvent(GR_EVENT_TYPE type, int buttons, MWKEYMOD modifiers)
{
GR_EVENT_MOUSE *ep; /* mouse motion event */
GR_WINDOW *wp; /* current window */
GR_EVENT_CLIENT *ecp; /* current event client */
GR_CLIENT *client; /* current client */
GR_WINDOW_ID subwid; /* subwindow id event is for */
GR_EVENT_MASK eventmask; /* event mask */
eventmask = GR_EVENTMASK(type);
if (eventmask == 0)
return;
wp = mousewp;
subwid = wp->id;
if (grabbuttonwp) {
while ((wp != rootwp) && (wp != grabbuttonwp))
wp = wp->parent;
#if 0
if (wp != grabbuttonwp)
subwid = grabbuttonwp->id;
#endif
wp = grabbuttonwp;
}
for (;;) {
for (ecp = wp->eventclients; ecp; ecp = ecp->next) {
if ((ecp->eventmask & eventmask) == 0)
continue;
client = ecp->client;
/*
* If the event is for just the latest position,
* then search the event queue for an existing
* event of this type (if any), and free it.
*/
if (type == GR_EVENT_TYPE_MOUSE_POSITION)
GsFreePositionEvent(client, wp->id, subwid);
ep = (GR_EVENT_MOUSE *) GsAllocEvent(client);
if (ep == NULL)
continue;
ep->type = type;
ep->wid = wp->id;
ep->subwid = subwid;
ep->rootx = cursorx;
ep->rooty = cursory;
ep->x = cursorx - wp->x;
ep->y = cursory - wp->y;
ep->buttons = buttons;
ep->modifiers = modifiers;
}
if ((wp == rootwp) || grabbuttonwp ||
(wp->nopropmask & eventmask))
return;
wp = wp->parent;
}
}
/*
* Deliver a keyboard event to one of the clients which have selected for it.
* Only the first client found gets the event (no duplicates are sent). The
* window the event is delivered to is either the smallest one containing
* the mouse coordinates, or else one of its direct ancestors (if such a
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -