1954 lines
52 KiB
C
1954 lines
52 KiB
C
/* event.c- event loop and handling
|
|
*
|
|
* Window Maker window manager
|
|
*
|
|
* Copyright (c) 1997-2003 Alfredo K. Kojima
|
|
* Copyright (c) 2014-2023 Window Maker Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include "wconfig.h"
|
|
|
|
#ifdef HAVE_INOTIFY
|
|
#include <sys/select.h>
|
|
#include <sys/inotify.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
|
|
#include "WindowMaker.h"
|
|
#include "window.h"
|
|
#include "actions.h"
|
|
#include "client.h"
|
|
#include "main.h"
|
|
#include "cycling.h"
|
|
#include "keybind.h"
|
|
#include "application.h"
|
|
#include "stacking.h"
|
|
#include "defaults.h"
|
|
#include "workspace.h"
|
|
#include "dock.h"
|
|
#include "framewin.h"
|
|
#include "properties.h"
|
|
#include "balloon.h"
|
|
#include "xinerama.h"
|
|
#include "rootmenu.h"
|
|
#include "colormap.h"
|
|
#include "screen.h"
|
|
#include "shutdown.h"
|
|
#include "misc.h"
|
|
#include "event.h"
|
|
#include "winmenu.h"
|
|
#include "switchmenu.h"
|
|
#include "wsmap.h"
|
|
|
|
|
|
#define MOD_MASK wPreferences.modifier_mask
|
|
|
|
/************ Local stuff ***********/
|
|
|
|
static void saveTimestamp(WMEvent *event);
|
|
static void handleColormapNotify(WMEvent *event);
|
|
static void handleMapNotify(WMEvent *event);
|
|
static void handleUnmapNotify(WMEvent *event);
|
|
static void handleButtonPress(WMEvent *event);
|
|
static void handleExpose(WMEvent *event);
|
|
static void handleDestroyNotify(WMEvent *event);
|
|
static void handleConfigureRequest(WMEvent *event);
|
|
static void handleMapRequest(WMEvent *event);
|
|
static void handlePropertyNotify(WMEvent *event);
|
|
static void handleEnterNotify(WMEvent *event);
|
|
static void handleLeaveNotify(WMEvent *event);
|
|
static void handleExtensions(WMEvent *event);
|
|
static void handleClientMessage(WMEvent *event);
|
|
static void handleKeyPress(WMEvent *event);
|
|
static void handleFocusIn(WMEvent *event);
|
|
static void handleMotionNotify(WMEvent *event);
|
|
static void handleVisibilityNotify(WMEvent *event);
|
|
/* inotify handling moved to x11_backend.c (x11_event_loop_run) */
|
|
static void wdelete_death_handler(WMagicNumber id);
|
|
|
|
|
|
/* real dead process handler */
|
|
static void handleDeadProcess(void);
|
|
|
|
typedef struct DeadProcesses {
|
|
pid_t pid;
|
|
unsigned char exit_status;
|
|
} DeadProcesses;
|
|
|
|
/* stack of dead processes */
|
|
static DeadProcesses deadProcesses[MAX_DEAD_PROCESSES];
|
|
static int deadProcessPtr = 0;
|
|
|
|
typedef struct DeathHandler {
|
|
WDeathHandler *callback;
|
|
pid_t pid;
|
|
void *client_data;
|
|
} DeathHandler;
|
|
|
|
static WMArray *deathHandlers = NULL;
|
|
|
|
WMagicNumber wAddDeathHandler(pid_t pid, WDeathHandler * callback, void *cdata)
|
|
{
|
|
DeathHandler *handler;
|
|
|
|
handler = malloc(sizeof(DeathHandler));
|
|
if (!handler)
|
|
return 0;
|
|
|
|
handler->pid = pid;
|
|
handler->callback = callback;
|
|
handler->client_data = cdata;
|
|
|
|
if (!deathHandlers)
|
|
deathHandlers = WMCreateArrayWithDestructor(8, free);
|
|
|
|
WMAddToArray(deathHandlers, handler);
|
|
|
|
return handler;
|
|
}
|
|
|
|
static void wdelete_death_handler(WMagicNumber id)
|
|
{
|
|
DeathHandler *handler = (DeathHandler *) id;
|
|
|
|
if (!handler || !deathHandlers)
|
|
return;
|
|
|
|
/* array destructor will call free(handler) */
|
|
WMRemoveFromArray(deathHandlers, handler);
|
|
}
|
|
|
|
void DispatchEvent(WMEvent * event)
|
|
{
|
|
|
|
if (deathHandlers)
|
|
handleDeadProcess();
|
|
|
|
if (WCHECK_STATE(WSTATE_NEED_EXIT)) {
|
|
WCHANGE_STATE(WSTATE_EXITING);
|
|
/* received SIGTERM */
|
|
/*
|
|
* WMHandleEvent() can't be called from anything
|
|
* executed inside here, or we can get in a infinite
|
|
* recursive loop.
|
|
*
|
|
* In Wayland mode, calling wl_display_destroy() from a signal
|
|
* handler while wl_event_loop_dispatch() is active causes a
|
|
* double-free (glibc detects it as SIGABRT). Instead, write a
|
|
* byte to the self-pipe; the event loop reads it and calls
|
|
* wl_display_terminate() from within loop context. EventLoop()
|
|
* then calls Exit(0) after wl_display_run() returns.
|
|
*/
|
|
wm_backend->event_handle_signal_exit();
|
|
|
|
} else if (WCHECK_STATE(WSTATE_NEED_RESTART)) {
|
|
WCHANGE_STATE(WSTATE_RESTARTING);
|
|
|
|
Shutdown(WSRestartPreparationMode);
|
|
/* received SIGHUP */
|
|
Restart(NULL, True);
|
|
} else if (WCHECK_STATE(WSTATE_NEED_REREAD)) {
|
|
WCHANGE_STATE(WSTATE_NORMAL);
|
|
wDefaultsCheckDomains(NULL);
|
|
}
|
|
|
|
/* for the case that all that is wanted to be dispatched is
|
|
* the stuff above */
|
|
if (!event)
|
|
return;
|
|
|
|
saveTimestamp(event);
|
|
switch (event->type) {
|
|
case WME_MAP_REQUEST:
|
|
handleMapRequest(event);
|
|
break;
|
|
|
|
case WME_KEY_PRESS:
|
|
handleKeyPress(event);
|
|
break;
|
|
|
|
case WME_MOTION:
|
|
handleMotionNotify(event);
|
|
break;
|
|
|
|
case WME_CONFIGURE_REQUEST:
|
|
handleConfigureRequest(event);
|
|
break;
|
|
|
|
case WME_DESTROY:
|
|
handleDestroyNotify(event);
|
|
break;
|
|
|
|
case WME_MAP:
|
|
handleMapNotify(event);
|
|
break;
|
|
|
|
case WME_UNMAP:
|
|
handleUnmapNotify(event);
|
|
break;
|
|
|
|
case WME_BUTTON_PRESS:
|
|
handleButtonPress(event);
|
|
break;
|
|
|
|
case WME_EXPOSE:
|
|
handleExpose(event);
|
|
break;
|
|
|
|
case WME_PROPERTY:
|
|
handlePropertyNotify(event);
|
|
break;
|
|
|
|
case WME_ENTER:
|
|
handleEnterNotify(event);
|
|
break;
|
|
|
|
case WME_LEAVE:
|
|
handleLeaveNotify(event);
|
|
break;
|
|
|
|
case WME_CLIENT_MESSAGE:
|
|
handleClientMessage(event);
|
|
break;
|
|
|
|
case WME_COLORMAP:
|
|
handleColormapNotify(event);
|
|
break;
|
|
|
|
case WME_MAPPING:
|
|
if (event->u.mapping.request == WM_MAPPING_KEYBOARD || event->u.mapping.request == WM_MAPPING_MODIFIER)
|
|
wm_backend->keyboard_mapping_refresh(event);
|
|
break;
|
|
|
|
case WME_FOCUS_IN:
|
|
handleFocusIn(event);
|
|
break;
|
|
|
|
case WME_VISIBILITY:
|
|
handleVisibilityNotify(event);
|
|
break;
|
|
|
|
case WME_CONFIGURE:
|
|
wm_backend->event_randr_update(event);
|
|
break;
|
|
|
|
case WME_SELECTION_REQUEST:
|
|
wm_backend->handle_selection_request(event);
|
|
break;
|
|
|
|
case WME_SELECTION_CLEAR:
|
|
wm_backend->handle_selection_clear(event);
|
|
break;
|
|
|
|
default:
|
|
handleExtensions(event);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
* EventLoop-
|
|
* Processes X and internal events indefinitely.
|
|
*
|
|
* Returns:
|
|
* Never returns
|
|
*
|
|
* Side effects:
|
|
* The LastTimestamp global variable is updated.
|
|
* Calls inotifyGetEvents if defaults database changes.
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
noreturn void EventLoop(void)
|
|
{
|
|
wm_backend->event_loop_run();
|
|
Exit(0);
|
|
}
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
* ProcessPendingEvents --
|
|
* Processes the events that are currently pending (at the time
|
|
* this function is called) in the display's queue.
|
|
*
|
|
* Returns:
|
|
* After the pending events that were present at the function call
|
|
* are processed.
|
|
*
|
|
* Side effects:
|
|
* Many -- whatever handling events may involve.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
void ProcessPendingEvents(void)
|
|
{
|
|
WMEvent event;
|
|
int count;
|
|
|
|
wm_backend->event_sync(False);
|
|
|
|
/* Take a snapshot of the event count in the queue */
|
|
count = wm_backend->event_pending();
|
|
|
|
while (count > 0 && wm_backend->event_pending()) {
|
|
WMNextEvent(wScreenWithNumber(0)->wmscreen, &event);
|
|
WMHandleEvent(&event);
|
|
count--;
|
|
}
|
|
}
|
|
|
|
Bool IsDoubleClick(WScreen * scr, WBackendEvent * event)
|
|
{
|
|
if ((scr->last_click_time > 0) &&
|
|
(event->u.button.time - scr->last_click_time <= wPreferences.dblclick_time)
|
|
&& (event->u.button.button == (unsigned int)scr->last_click_button)
|
|
&& (event->u.button.window == scr->last_click_window)) {
|
|
|
|
scr->flags.next_click_is_not_double = 1;
|
|
scr->last_click_time = 0;
|
|
scr->last_click_window = event->u.button.window;
|
|
|
|
return True;
|
|
}
|
|
return False;
|
|
}
|
|
|
|
void NotifyDeadProcess(pid_t pid, unsigned char status)
|
|
{
|
|
if (deadProcessPtr >= MAX_DEAD_PROCESSES - 1) {
|
|
wwarning("stack overflow: too many dead processes");
|
|
return;
|
|
}
|
|
/* stack the process to be handled later,
|
|
* as this is called from the signal handler */
|
|
deadProcesses[deadProcessPtr].pid = pid;
|
|
deadProcesses[deadProcessPtr].exit_status = status;
|
|
deadProcessPtr++;
|
|
}
|
|
|
|
static void handleDeadProcess(void)
|
|
{
|
|
DeathHandler *tmp;
|
|
int i;
|
|
|
|
for (i = 0; i < deadProcessPtr; i++) {
|
|
wWindowDeleteSavedStatesForPID(deadProcesses[i].pid);
|
|
}
|
|
|
|
if (!deathHandlers) {
|
|
deadProcessPtr = 0;
|
|
return;
|
|
}
|
|
|
|
/* get the pids on the queue and call handlers */
|
|
while (deadProcessPtr > 0) {
|
|
deadProcessPtr--;
|
|
|
|
for (i = WMGetArrayItemCount(deathHandlers) - 1; i >= 0; i--) {
|
|
tmp = WMGetFromArray(deathHandlers, i);
|
|
if (!tmp)
|
|
continue;
|
|
|
|
if (tmp->pid == deadProcesses[deadProcessPtr].pid) {
|
|
(*tmp->callback) (tmp->pid,
|
|
deadProcesses[deadProcessPtr].exit_status, tmp->client_data);
|
|
wdelete_death_handler(tmp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void saveTimestamp(WMEvent * event)
|
|
{
|
|
/*
|
|
* Never save WM_CURRENT_TIME as LastTimestamp because WM_CURRENT_TIME
|
|
* it's not a real timestamp (it's the 0L constant)
|
|
*/
|
|
|
|
switch (event->type) {
|
|
case WME_BUTTON_RELEASE:
|
|
case WME_BUTTON_PRESS:
|
|
w_global.timestamp.last_event = event->u.button.time;
|
|
break;
|
|
case WME_KEY_PRESS:
|
|
case WME_KEY_RELEASE:
|
|
w_global.timestamp.last_event = event->u.key.time;
|
|
break;
|
|
case WME_MOTION:
|
|
w_global.timestamp.last_event = event->u.motion.time;
|
|
break;
|
|
case WME_PROPERTY:
|
|
w_global.timestamp.last_event = event->u.property.time;
|
|
break;
|
|
case WME_ENTER:
|
|
case WME_LEAVE:
|
|
w_global.timestamp.last_event = event->u.crossing.time;
|
|
break;
|
|
case WME_SELECTION_CLEAR:
|
|
case WME_SELECTION_REQUEST:
|
|
case WME_SELECTION:
|
|
w_global.timestamp.last_event = event->u.selection.time;
|
|
if (event->type == WME_SELECTION)
|
|
wm_backend->xdnd_process_selection(event);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int matchWindow(const void *item, const void *cdata)
|
|
{
|
|
return (((WFakeGroupLeader *) item)->origLeader == (WNativeWindow)(unsigned long) cdata);
|
|
}
|
|
|
|
static void handleExtensions(WMEvent * event)
|
|
{
|
|
wm_backend->event_handle_extensions(event);
|
|
}
|
|
|
|
static void handleMapRequest(WMEvent * ev)
|
|
{
|
|
WWindow *wwin;
|
|
WScreen *scr = NULL;
|
|
WNativeWindow window = ev->u.mapRequest.window;
|
|
|
|
wwin = wWindowFor(window);
|
|
if (wwin != NULL) {
|
|
if (wwin->flags.shaded) {
|
|
wUnshadeWindow(wwin);
|
|
}
|
|
/* deiconify window */
|
|
if (wwin->flags.miniaturized) {
|
|
wDeiconifyWindow(wwin);
|
|
} else if (wwin->flags.hidden) {
|
|
WApplication *wapp = wApplicationOf(wwin->main_window);
|
|
/* go to the last workspace that the user worked on the app */
|
|
if (wapp) {
|
|
wWorkspaceChange(wwin->screen_ptr, wapp->last_workspace);
|
|
}
|
|
wUnhideApplication(wapp, False, False);
|
|
}
|
|
return;
|
|
}
|
|
|
|
scr = wScreenForRootWindow(ev->u.mapRequest.parent);
|
|
|
|
wwin = wManageWindow(scr, window);
|
|
|
|
/*
|
|
* This is to let the Dock know that the application it launched
|
|
* has already been mapped (eg: it has finished launching).
|
|
* It is not necessary for normally docked apps, but is needed for
|
|
* apps that were forcedly docked (like with dockit).
|
|
*/
|
|
if (scr->last_dock) {
|
|
if (wwin && wwin->main_window != WM_NATIVE_NONE && wwin->main_window != window)
|
|
wDockTrackWindowLaunch(scr->last_dock, wwin->main_window);
|
|
else
|
|
wDockTrackWindowLaunch(scr->last_dock, window);
|
|
}
|
|
|
|
if (wwin) {
|
|
wClientSetState(wwin, WM_STATE_NORMAL, WM_NATIVE_NONE);
|
|
if (wwin->flags.maximized) {
|
|
wMaximizeWindow(wwin, wwin->flags.maximized,
|
|
wGetHeadForWindow(wwin));
|
|
}
|
|
if (wwin->flags.shaded) {
|
|
wwin->flags.shaded = 0;
|
|
wwin->flags.skip_next_animation = 1;
|
|
wShadeWindow(wwin);
|
|
}
|
|
if (wwin->flags.miniaturized) {
|
|
wwin->flags.miniaturized = 0;
|
|
wwin->flags.skip_next_animation = 1;
|
|
wIconifyWindow(wwin);
|
|
}
|
|
if (wwin->flags.fullscreen) {
|
|
wwin->flags.fullscreen = 0;
|
|
wFullscreenWindow(wwin);
|
|
}
|
|
if (wwin->flags.hidden) {
|
|
WApplication *wapp = wApplicationOf(wwin->main_window);
|
|
|
|
wwin->flags.hidden = 0;
|
|
wwin->flags.skip_next_animation = 1;
|
|
if (wapp) {
|
|
wHideApplication(wapp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void handleDestroyNotify(WMEvent * event)
|
|
{
|
|
WWindow *wwin;
|
|
WApplication *app;
|
|
WNativeWindow window = event->u.lifecycle.window;
|
|
WScreen *scr = wScreenForRootWindow(event->u.lifecycle.event);
|
|
int widx;
|
|
|
|
wwin = wWindowFor(window);
|
|
if (wwin) {
|
|
wUnmanageWindow(wwin, False, True);
|
|
}
|
|
|
|
if (scr != NULL) {
|
|
while ((widx = WMFindInArray(scr->fakeGroupLeaders, matchWindow, (void *)window)) != WANotFound) {
|
|
WFakeGroupLeader *fPtr;
|
|
|
|
fPtr = WMGetFromArray(scr->fakeGroupLeaders, widx);
|
|
if (fPtr->retainCount > 0) {
|
|
fPtr->retainCount--;
|
|
if (fPtr->retainCount == 0 && fPtr->leader != WM_NATIVE_NONE) {
|
|
wm_backend->frame_destroy(NULL, (WNativeWindow)fPtr->leader);
|
|
fPtr->leader = WM_NATIVE_NONE;
|
|
wm_backend->event_flush();
|
|
}
|
|
}
|
|
fPtr->origLeader = WM_NATIVE_NONE;
|
|
}
|
|
}
|
|
|
|
app = wApplicationOf(window);
|
|
if (app) {
|
|
if (window == app->main_window) {
|
|
app->refcount = 0;
|
|
wwin = app->main_window_desc->screen_ptr->focused_window;
|
|
while (wwin) {
|
|
if (wwin->main_window == window) {
|
|
wwin->main_window = WM_NATIVE_NONE;
|
|
}
|
|
wwin = wwin->prev;
|
|
}
|
|
}
|
|
wApplicationDestroy(app);
|
|
}
|
|
}
|
|
|
|
static void handleExpose(WMEvent * event)
|
|
{
|
|
WObjDescriptor *desc;
|
|
|
|
wm_backend->event_flush_window((WNativeWindow)event->u.expose.window, WME_EXPOSE);
|
|
|
|
desc = wm_backend->context_find(event->u.expose.window, WM_CTX_CLIENT_WIN);
|
|
if (!desc) {
|
|
return;
|
|
}
|
|
|
|
if (desc->handle_expose) {
|
|
(*desc->handle_expose) (desc, event);
|
|
}
|
|
}
|
|
|
|
static void executeWheelAction(WScreen *scr, WMEvent *event, int action)
|
|
{
|
|
WWindow *wwin;
|
|
Bool next_direction;
|
|
|
|
if (event->u.button.button == Button5 || event->u.button.button == Button6)
|
|
next_direction = False;
|
|
else
|
|
next_direction = True;
|
|
|
|
switch (action) {
|
|
case WA_SWITCH_WORKSPACES:
|
|
if (next_direction)
|
|
wWorkspaceRelativeChange(scr, 1);
|
|
else
|
|
wWorkspaceRelativeChange(scr, -1);
|
|
break;
|
|
|
|
case WA_SWITCH_WINDOWS:
|
|
wwin = scr->focused_window;
|
|
if (next_direction)
|
|
wWindowFocusNext(wwin, True);
|
|
else
|
|
wWindowFocusPrev(wwin, True);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void executeButtonAction(WScreen *scr, WMEvent *event, int action)
|
|
{
|
|
WWindow *wwin;
|
|
|
|
switch (action) {
|
|
case WA_SELECT_WINDOWS:
|
|
wUnselectWindows(scr);
|
|
wSelectWindows(scr, event->u.button.x_root, event->u.button.y_root);
|
|
if (wPreferences.close_rootmenu_left_right_click){
|
|
WMenu *menu = NULL;
|
|
WMPropList *definition;
|
|
menu = scr->root_menu;
|
|
if (scr->root_menu){
|
|
wMenuDestroy(menu,True);
|
|
scr->root_menu = NULL;
|
|
definition = w_global.domain.root_menu->dictionary;
|
|
menu = configureMenu(scr, definition);
|
|
scr->root_menu = menu;
|
|
}
|
|
}
|
|
break;
|
|
case WA_OPEN_APPMENU:
|
|
OpenRootMenu(scr, event->u.button.x_root, event->u.button.y_root, False);
|
|
/* ugly hack */
|
|
if (scr->root_menu) {
|
|
if (scr->root_menu->brother->flags.mapped)
|
|
event->u.button.window = scr->root_menu->brother->frame->core->window;
|
|
else
|
|
event->u.button.window = scr->root_menu->frame->core->window;
|
|
}
|
|
break;
|
|
case WA_OPEN_WINLISTMENU:
|
|
OpenSwitchMenu(scr, event->u.button.x_root, event->u.button.y_root, False);
|
|
if (scr->switch_menu) {
|
|
if (scr->switch_menu->brother->flags.mapped)
|
|
event->u.button.window = scr->switch_menu->brother->frame->core->window;
|
|
else
|
|
event->u.button.window = scr->switch_menu->frame->core->window;
|
|
}
|
|
break;
|
|
case WA_MOVE_PREVWORKSPACE:
|
|
wWorkspaceRelativeChange(scr, -1);
|
|
break;
|
|
case WA_MOVE_NEXTWORKSPACE:
|
|
wWorkspaceRelativeChange(scr, 1);
|
|
break;
|
|
case WA_MOVE_PREVWINDOW:
|
|
wwin = scr->focused_window;
|
|
wWindowFocusPrev(wwin, True);
|
|
break;
|
|
case WA_MOVE_NEXTWINDOW:
|
|
wwin = scr->focused_window;
|
|
wWindowFocusNext(wwin, True);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* bindable */
|
|
static void handleButtonPress(WMEvent * event)
|
|
{
|
|
WObjDescriptor *desc;
|
|
WScreen *scr;
|
|
|
|
scr = wScreenForRootWindow(event->u.button.window);
|
|
if (!scr)
|
|
scr = wScreenWithNumber(0);
|
|
|
|
#ifdef BALLOON_TEXT
|
|
wBalloonHide(scr);
|
|
#endif
|
|
|
|
if (!wPreferences.disable_root_mouse && event->u.button.window == scr->root_win) {
|
|
if (event->u.button.button == Button1 && wPreferences.mouse_button1 != WA_NONE) {
|
|
executeButtonAction(scr, event, wPreferences.mouse_button1);
|
|
} else if (event->u.button.button == Button2 && wPreferences.mouse_button2 != WA_NONE) {
|
|
executeButtonAction(scr, event, wPreferences.mouse_button2);
|
|
} else if (event->u.button.button == Button3 && wPreferences.mouse_button3 != WA_NONE) {
|
|
executeButtonAction(scr, event, wPreferences.mouse_button3);
|
|
} else if (event->u.button.button == Button8 && wPreferences.mouse_button8 != WA_NONE) {
|
|
executeButtonAction(scr, event, wPreferences.mouse_button8);
|
|
}else if (event->u.button.button == Button9 && wPreferences.mouse_button9 != WA_NONE) {
|
|
executeButtonAction(scr, event, wPreferences.mouse_button9);
|
|
} else if (event->u.button.button == Button4 && wPreferences.mouse_wheel_scroll != WA_NONE) {
|
|
executeWheelAction(scr, event, wPreferences.mouse_wheel_scroll);
|
|
} else if (event->u.button.button == Button5 && wPreferences.mouse_wheel_scroll != WA_NONE) {
|
|
executeWheelAction(scr, event, wPreferences.mouse_wheel_scroll);
|
|
} else if (event->u.button.button == Button6 && wPreferences.mouse_wheel_tilt != WA_NONE) {
|
|
executeWheelAction(scr, event, wPreferences.mouse_wheel_tilt);
|
|
} else if (event->u.button.button == Button7 && wPreferences.mouse_wheel_tilt != WA_NONE) {
|
|
executeWheelAction(scr, event, wPreferences.mouse_wheel_tilt);
|
|
}
|
|
}
|
|
|
|
desc = wm_backend->context_find(event->u.button.window, WM_CTX_CLIENT_WIN);
|
|
if (!desc)
|
|
desc = wm_backend->context_find(event->u.button.window, WM_CTX_CLIENT_WIN);
|
|
if (!desc)
|
|
return;
|
|
|
|
if (desc->parent_type == WCLASS_WINDOW) {
|
|
wm_backend->event_sync(0);
|
|
|
|
if (event->u.button.state & ( MOD_MASK | WM_MOD_CONTROL )) {
|
|
wm_backend->event_allow_async();
|
|
} else {
|
|
/* if (wPreferences.focus_mode == WKF_CLICK) { */
|
|
if (wPreferences.ignore_focus_click) {
|
|
wm_backend->event_allow_async();
|
|
}
|
|
wm_backend->event_replay_pointer();
|
|
/* } */
|
|
}
|
|
wm_backend->event_sync(0);
|
|
} else if (desc->parent_type == WCLASS_APPICON
|
|
|| desc->parent_type == WCLASS_MINIWINDOW || desc->parent_type == WCLASS_DOCK_ICON) {
|
|
if (event->u.button.state & MOD_MASK) {
|
|
wm_backend->event_sync(0);
|
|
wm_backend->event_allow_async();
|
|
wm_backend->event_sync(0);
|
|
}
|
|
}
|
|
|
|
if (desc->handle_mousedown != NULL) {
|
|
(*desc->handle_mousedown) (desc, event);
|
|
}
|
|
|
|
/* save double-click information */
|
|
if (scr->flags.next_click_is_not_double) {
|
|
scr->flags.next_click_is_not_double = 0;
|
|
} else {
|
|
scr->last_click_time = event->u.button.time;
|
|
scr->last_click_button = event->u.button.button;
|
|
scr->last_click_window = event->u.button.window;
|
|
}
|
|
}
|
|
|
|
static void handleMapNotify(WMEvent * event)
|
|
{
|
|
WWindow *wwin;
|
|
|
|
wwin = wWindowFor(event->u.lifecycle.event);
|
|
if (wwin && wwin->client_win == event->u.lifecycle.event) {
|
|
if (wwin->flags.miniaturized) {
|
|
wDeiconifyWindow(wwin);
|
|
} else {
|
|
wm_backend->server_grab();
|
|
wWindowMap(wwin);
|
|
wClientSetState(wwin, WM_STATE_NORMAL, WM_NATIVE_NONE);
|
|
wm_backend->server_ungrab();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void handleUnmapNotify(WMEvent * event)
|
|
{
|
|
WWindow *wwin;
|
|
Bool withdraw = False;
|
|
|
|
/* only process windows with StructureNotify selected
|
|
* (ignore SubstructureNotify) */
|
|
wwin = wWindowFor(event->u.lifecycle.window);
|
|
if (!wwin)
|
|
return;
|
|
|
|
/* whether the event is a Withdrawal request */
|
|
if (event->u.lifecycle.event == wwin->screen_ptr->root_win) {
|
|
withdraw = True;
|
|
}
|
|
|
|
if (wwin->client_win != event->u.lifecycle.window && !withdraw)
|
|
return;
|
|
|
|
if (!wwin->flags.mapped && !withdraw
|
|
&& WIN_WORKSPACE(wwin) == wwin->screen_ptr->current_workspace
|
|
&& !wwin->flags.miniaturized && !wwin->flags.hidden)
|
|
return;
|
|
|
|
wm_backend->server_grab();
|
|
wm_backend->window_unmap((WNativeWindow)wwin->frame->core->window);
|
|
wwin->flags.mapped = 0;
|
|
wm_backend->event_sync(0);
|
|
/* If a destroy event for the client window is already pending, skip
|
|
* the unmanage here — handleDestroyNotify will run it on the next
|
|
* event-loop iteration. (Pre-Phase-30 code dispatched the destroy
|
|
* synchronously here via x11_translate_event(); that path used an
|
|
* uninitialized XEvent and never actually carried the destroy data,
|
|
* so it became dead code. Letting the destroy flow through the
|
|
* normal dispatch is the correct fix.) */
|
|
if (!wm_backend->event_check_window((WNativeWindow)wwin->client_win,
|
|
WME_DESTROY)) {
|
|
Bool reparented = False;
|
|
|
|
if (wm_backend->event_check_window((WNativeWindow)wwin->client_win, WME_REPARENT))
|
|
reparented = True;
|
|
|
|
/* withdraw window */
|
|
wwin->flags.mapped = 0;
|
|
if (!reparented)
|
|
wClientSetState(wwin, WM_STATE_WITHDRAWN, WM_NATIVE_NONE);
|
|
|
|
/* if the window was reparented, do not reparent it back to the
|
|
* root window */
|
|
wUnmanageWindow(wwin, !reparented, False);
|
|
}
|
|
wm_backend->server_ungrab();
|
|
}
|
|
|
|
static void handleConfigureRequest(WMEvent * event)
|
|
{
|
|
WWindow *wwin;
|
|
|
|
wwin = wWindowFor(event->u.configureRequest.window);
|
|
if (wwin == NULL) {
|
|
/*
|
|
* Configure request for unmapped window
|
|
*/
|
|
wClientConfigure(NULL, event);
|
|
} else {
|
|
wClientConfigure(wwin, event);
|
|
}
|
|
}
|
|
|
|
static void handlePropertyNotify(WMEvent * event)
|
|
{
|
|
WWindow *wwin;
|
|
WApplication *wapp;
|
|
WNativeWindow jr;
|
|
int ji;
|
|
unsigned int ju;
|
|
wwin = wWindowFor(event->u.property.window);
|
|
|
|
if (wwin) {
|
|
if (!wm_backend->window_exists((WNativeWindow)wwin->client_win)) {
|
|
return;
|
|
}
|
|
wClientCheckProperty(wwin, event);
|
|
}
|
|
wapp = wApplicationOf(event->u.property.window);
|
|
if (wapp) {
|
|
wClientCheckProperty(wapp->main_window_desc, event);
|
|
}
|
|
}
|
|
|
|
static void handleClientMessage(WMEvent * event)
|
|
{
|
|
WWindow *wwin;
|
|
WObjDescriptor *desc;
|
|
|
|
/* handle transition from Normal to Iconic state */
|
|
if (event->u.clientMessage.message_type == w_global.atom.wm.change_state
|
|
&& event->u.clientMessage.format == 32 && event->u.clientMessage.data.l[0] == WM_STATE_ICONIC) {
|
|
|
|
wwin = wWindowFor(event->u.clientMessage.window);
|
|
if (!wwin)
|
|
return;
|
|
if (!wwin->flags.miniaturized)
|
|
wIconifyWindow(wwin);
|
|
} else if (event->u.clientMessage.message_type == w_global.atom.wm.colormap_notify && event->u.clientMessage.format == 32) {
|
|
WScreen *scr = wScreenForRootWindow(event->u.clientMessage.window);
|
|
|
|
if (!scr)
|
|
return;
|
|
|
|
if (event->u.clientMessage.data.l[1] == 1) { /* starting */
|
|
wColormapAllowClientInstallation(scr, True);
|
|
} else { /* stopping */
|
|
wColormapAllowClientInstallation(scr, False);
|
|
}
|
|
} else if (event->u.clientMessage.message_type == w_global.atom.wmaker.command) {
|
|
|
|
char *command;
|
|
size_t len;
|
|
|
|
len = sizeof(event->u.clientMessage.data.b);
|
|
command = wmalloc(len + 1);
|
|
strncpy(command, event->u.clientMessage.data.b, len);
|
|
|
|
if (strncmp(command, "Reconfigure", sizeof("Reconfigure")) == 0) {
|
|
wwarning(_("Got Reconfigure command"));
|
|
wDefaultsCheckDomains(NULL);
|
|
} else {
|
|
wwarning(_("Got unknown command %s"), command);
|
|
}
|
|
|
|
wfree(command);
|
|
|
|
} else if (event->u.clientMessage.message_type == w_global.atom.wmaker.wm_function) {
|
|
WApplication *wapp;
|
|
int done = 0;
|
|
wapp = wApplicationOf(event->u.clientMessage.window);
|
|
if (wapp) {
|
|
switch (event->u.clientMessage.data.l[0]) {
|
|
case WMFHideOtherApplications:
|
|
wHideOtherApplications(wapp->main_window_desc);
|
|
done = 1;
|
|
break;
|
|
|
|
case WMFHideApplication:
|
|
wHideApplication(wapp);
|
|
done = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (!done) {
|
|
wwin = wWindowFor(event->u.clientMessage.window);
|
|
if (wwin) {
|
|
switch (event->u.clientMessage.data.l[0]) {
|
|
case WMFHideOtherApplications:
|
|
wHideOtherApplications(wwin);
|
|
break;
|
|
|
|
case WMFHideApplication:
|
|
wHideApplication(wApplicationOf(wwin->main_window));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else if (event->u.clientMessage.message_type == w_global.atom.gnustep.wm_attr) {
|
|
wwin = wWindowFor(event->u.clientMessage.window);
|
|
if (!wwin)
|
|
return;
|
|
switch (event->u.clientMessage.data.l[0]) {
|
|
case GSWindowLevelAttr:
|
|
{
|
|
int level = (int)event->u.clientMessage.data.l[1];
|
|
|
|
if (WINDOW_LEVEL(wwin) != level) {
|
|
ChangeStackingLevel(wwin->frame->core, level);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
} else if (event->u.clientMessage.message_type == w_global.atom.gnustep.titlebar_state) {
|
|
wwin = wWindowFor(event->u.clientMessage.window);
|
|
if (!wwin)
|
|
return;
|
|
switch (event->u.clientMessage.data.l[0]) {
|
|
case WMTitleBarNormal:
|
|
wFrameWindowChangeState(wwin->frame, WS_UNFOCUSED);
|
|
break;
|
|
case WMTitleBarMain:
|
|
wFrameWindowChangeState(wwin->frame, WS_PFOCUSED);
|
|
break;
|
|
case WMTitleBarKey:
|
|
wFrameWindowChangeState(wwin->frame, WS_FOCUSED);
|
|
break;
|
|
}
|
|
} else if (event->u.clientMessage.message_type == w_global.atom.wm.ignore_focus_events) {
|
|
WScreen *scr = wScreenForRootWindow(event->u.clientMessage.window);
|
|
if (!scr)
|
|
return;
|
|
scr->flags.ignore_focus_events = event->u.clientMessage.data.l[0] ? 1 : 0;
|
|
} else if (wm_backend->netwm_process_client_message(event)) {
|
|
/* do nothing */
|
|
} else if (wm_backend->xdnd_process_client_message(event)) {
|
|
/* do nothing */
|
|
} else {
|
|
/*
|
|
* Non-standard thing, but needed by OffiX DND.
|
|
* For when the icon frame gets a ClientMessage
|
|
* that should have gone to the icon_window.
|
|
*/
|
|
if ((desc = wm_backend->context_find(event->u.clientMessage.window, WM_CTX_CLIENT_WIN)) != NULL) {
|
|
struct WIcon *icon = NULL;
|
|
|
|
if (desc->parent_type == WCLASS_MINIWINDOW) {
|
|
icon = (WIcon *) desc->parent;
|
|
} else if (desc->parent_type == WCLASS_DOCK_ICON || desc->parent_type == WCLASS_APPICON) {
|
|
icon = ((WAppIcon *) desc->parent)->icon;
|
|
}
|
|
if (icon && (wwin = icon->owner)) {
|
|
if (wwin->client_win != event->u.clientMessage.window)
|
|
wm_backend->client_message_forward(event, wwin->client_win);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void raiseWindow(WScreen * scr)
|
|
{
|
|
WWindow *wwin;
|
|
|
|
scr->autoRaiseTimer = NULL;
|
|
|
|
wwin = wWindowFor(scr->autoRaiseWindow);
|
|
if (!wwin)
|
|
return;
|
|
|
|
if (!wwin->flags.destroyed && wwin->flags.focused) {
|
|
wRaiseFrame(wwin->frame->core);
|
|
/* this is needed or a race condition will occur */
|
|
wm_backend->event_sync(False);
|
|
}
|
|
}
|
|
|
|
static void handleEnterNotify(WMEvent * event)
|
|
{
|
|
WWindow *wwin;
|
|
WObjDescriptor *desc = NULL;
|
|
WScreen *scr = wScreenForRootWindow(event->u.crossing.window);
|
|
|
|
/*
|
|
* If a Leave event for this window is already pending, the pointer
|
|
* has already left — skip the enter handler. Pre-Phase-30 code
|
|
* peeked the leave event details and only skipped when mode/detail
|
|
* matched; that path went through an x11_translate_event() with an
|
|
* uninitialized XEvent and was therefore dead code (always reading
|
|
* stack garbage). Treating any pending Leave as cause to skip is
|
|
* the conservative correct behaviour.
|
|
*/
|
|
if (wm_backend->event_check_window((WNativeWindow)event->u.crossing.window, WME_LEAVE)) {
|
|
return;
|
|
}
|
|
|
|
desc = wm_backend->context_find(event->u.crossing.window, WM_CTX_CLIENT_WIN);
|
|
if (desc) {
|
|
if (desc->handle_enternotify) {
|
|
(*desc->handle_enternotify) (desc, event);
|
|
}
|
|
}
|
|
|
|
/* enter to window */
|
|
wwin = wWindowFor(event->u.crossing.window);
|
|
if (!wwin) {
|
|
if (wPreferences.colormap_mode == WCM_POINTER) {
|
|
wm_backend->colormap_install(scr, NULL);
|
|
}
|
|
if (scr->autoRaiseTimer && event->u.crossing.window == event->u.crossing.window) {
|
|
WMDeleteTimerHandler(scr->autoRaiseTimer);
|
|
scr->autoRaiseTimer = NULL;
|
|
}
|
|
} else {
|
|
/* set auto raise timer even if in focus-follows-mouse mode
|
|
* and the event is for the frame window, even if the window
|
|
* has focus already. useful if you move the pointer from a focused
|
|
* window to the root window and back pretty fast
|
|
*
|
|
* set focus if in focus-follows-mouse mode and the event
|
|
* is for the frame window and window doesn't have focus yet */
|
|
if (wPreferences.focus_mode == WKF_SLOPPY
|
|
&& wwin->frame->core->window == event->u.crossing.window && !scr->flags.doing_alt_tab) {
|
|
|
|
if (!wwin->flags.focused && !WFLAGP(wwin, no_focusable))
|
|
wSetFocusTo(scr, wwin);
|
|
|
|
if (scr->autoRaiseTimer)
|
|
WMDeleteTimerHandler(scr->autoRaiseTimer);
|
|
scr->autoRaiseTimer = NULL;
|
|
|
|
if (wPreferences.raise_delay && !WFLAGP(wwin, no_focusable)) {
|
|
scr->autoRaiseWindow = wwin->frame->core->window;
|
|
scr->autoRaiseTimer
|
|
= WMAddTimerHandler(wPreferences.raise_delay, (WMCallback *) raiseWindow, scr);
|
|
}
|
|
}
|
|
/* Install colormap for window, if the colormap installation mode
|
|
* is colormap_follows_mouse */
|
|
if (wPreferences.colormap_mode == WCM_POINTER) {
|
|
if (wwin->client_win == event->u.crossing.window)
|
|
wm_backend->colormap_install(scr, wwin);
|
|
else
|
|
wm_backend->colormap_install(scr, NULL);
|
|
}
|
|
}
|
|
|
|
if (event->u.crossing.window == event->u.crossing.window
|
|
&& event->u.crossing.detail == WM_NOTIFY_NORMAL
|
|
&& event->u.crossing.detail != WM_NOTIFY_INFERIOR && wPreferences.focus_mode != WKF_CLICK) {
|
|
|
|
wSetFocusTo(scr, scr->focused_window);
|
|
}
|
|
#ifdef BALLOON_TEXT
|
|
wBalloonEnteredObject(scr, desc);
|
|
#endif
|
|
}
|
|
|
|
static void handleLeaveNotify(WMEvent * event)
|
|
{
|
|
WObjDescriptor *desc = NULL;
|
|
|
|
desc = wm_backend->context_find(event->u.crossing.window, WM_CTX_CLIENT_WIN);
|
|
if (desc) {
|
|
if (desc->handle_leavenotify) {
|
|
(*desc->handle_leavenotify) (desc, event);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void handleColormapNotify(WMEvent * event)
|
|
{
|
|
WWindow *wwin;
|
|
WScreen *scr;
|
|
Bool reinstall = False;
|
|
|
|
wwin = wWindowFor(event->u.colormap.window);
|
|
if (!wwin)
|
|
return;
|
|
|
|
scr = wwin->screen_ptr;
|
|
|
|
do {
|
|
if (wwin) {
|
|
if (event->u.colormap.c_new) {
|
|
WMNativeColormap cmap = 0;
|
|
|
|
wm_backend->window_get_colormap((WNativeWindow)wwin->client_win, &cmap);
|
|
|
|
if (wwin == scr->cmap_window && wwin->cmap_window_no == 0)
|
|
scr->current_colormap = cmap;
|
|
|
|
reinstall = True;
|
|
} else if (event->u.colormap.state == ColormapUninstalled &&
|
|
scr->current_colormap == event->u.colormap.colormap) {
|
|
|
|
/* some bastard app (like XV) removed our colormap */
|
|
reinstall = False;
|
|
} else if (event->u.colormap.state == ColormapInstalled &&
|
|
scr->current_colormap == event->u.colormap.colormap) {
|
|
|
|
/* someone has put our colormap back */
|
|
reinstall = False;
|
|
}
|
|
}
|
|
} while (wm_backend->event_check_type(WME_COLORMAP, event)
|
|
&& ((wwin = wWindowFor(event->u.colormap.window)) || 1));
|
|
|
|
if (reinstall && scr->current_colormap != WM_NATIVE_NONE) {
|
|
if (!scr->flags.colormap_stuff_blocked)
|
|
wm_backend->colormap_install(scr, NULL);
|
|
}
|
|
}
|
|
|
|
static void handleFocusIn(WMEvent * event)
|
|
{
|
|
WWindow *wwin;
|
|
|
|
/*
|
|
* For applications that like stealing the focus.
|
|
*/
|
|
wm_backend->event_flush_type(WME_FOCUS_IN);
|
|
saveTimestamp(event);
|
|
if (event->u.focus.mode == WM_NOTIFY_UNGRAB
|
|
|| event->u.focus.mode == WM_NOTIFY_GRAB || event->u.focus.detail > WM_NOTIFY_NONLINEAR_VIRTUAL) {
|
|
return;
|
|
}
|
|
|
|
wwin = wWindowFor(event->u.focus.window);
|
|
if (wwin && !wwin->flags.focused) {
|
|
if (wwin->flags.mapped)
|
|
wSetFocusTo(wwin->screen_ptr, wwin);
|
|
else
|
|
wSetFocusTo(wwin->screen_ptr, NULL);
|
|
} else if (!wwin) {
|
|
WScreen *scr = wScreenForWindow(event->u.focus.window);
|
|
if (scr)
|
|
wSetFocusTo(scr, NULL);
|
|
}
|
|
}
|
|
|
|
static WWindow *windowUnderPointer(WScreen * scr)
|
|
{
|
|
unsigned int mask;
|
|
int foo;
|
|
WNativeWindow bar, win;
|
|
|
|
win = (WNativeWindow)wm_backend->pointer_query_window((WNativeWindow)scr->root_win); if (win)
|
|
return wWindowFor(win);
|
|
return NULL;
|
|
}
|
|
|
|
static int CheckFullScreenWindowFocused(WScreen * scr)
|
|
{
|
|
if (scr->focused_window && scr->focused_window->flags.fullscreen)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Phase 78: Wayland keyboard event dispatch.
|
|
* Called from wl_backend's keyboard key listener with the XKB keycode and
|
|
* modifier mask. Scans wKeyBindings and dispatches the matching WM action.
|
|
* Returns 1 if consumed (WM shortcut), 0 if the event should be forwarded.
|
|
*/
|
|
int wWaylandKeyPress(unsigned int keycode, unsigned int modifiers)
|
|
{
|
|
WScreen *scr = wScreenWithNumber(0);
|
|
if (!scr)
|
|
return 0;
|
|
|
|
WWindow *wwin = scr->focused_window;
|
|
int modifiers_filtered = (int)(modifiers & (unsigned int)w_global.shortcut.modifiers_mask);
|
|
int command = -1;
|
|
short i;
|
|
|
|
for (i = 0; i < WKBD_LAST; i++) {
|
|
if (wKeyBindings[i].keycode == 0)
|
|
continue;
|
|
if ((unsigned int)wKeyBindings[i].keycode == keycode &&
|
|
wKeyBindings[i].modifier == modifiers_filtered) {
|
|
command = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (command < 0)
|
|
return 0; /* not a WM shortcut */
|
|
|
|
/* Reuse the ISMAPPED/ISFOCUSED macros defined inside handleKeyPress below. */
|
|
#ifndef ISMAPPED
|
|
#define ISMAPPED(w) ((w) && !(w)->flags.miniaturized && ((w)->flags.mapped || (w)->flags.shaded))
|
|
#define ISFOCUSED(w) ((w) && (w)->flags.focused)
|
|
#endif
|
|
|
|
/* Dispatch WM shortcuts — identical to handleKeyPress switch but without
|
|
* XEvent arguments. Only workspace/window commands that don't need an
|
|
* XEvent are included; pointer-position shortcuts use the virtual pointer. */
|
|
switch (command) {
|
|
case WKBD_MINIATURIZE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
wIconifyWindow(wwin);
|
|
}
|
|
break;
|
|
case WKBD_HIDE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
|
|
WApplication *wapp = wApplicationOf(wwin->main_window);
|
|
CloseWindowMenu(scr);
|
|
if (wapp && !WFLAGP(wapp->main_window_desc, no_appicon))
|
|
wHideApplication(wapp);
|
|
}
|
|
break;
|
|
case WKBD_MAXIMIZE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
wMaximizeWindow(wwin, MAX_VERTICAL | MAX_HORIZONTAL,
|
|
wGetHeadForWindow(wwin));
|
|
}
|
|
break;
|
|
case WKBD_VMAXIMIZE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
wMaximizeWindow(wwin, MAX_VERTICAL,
|
|
wGetHeadForWindow(wwin));
|
|
}
|
|
break;
|
|
case WKBD_HMAXIMIZE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
wMaximizeWindow(wwin, MAX_HORIZONTAL,
|
|
wGetHeadForWindow(wwin));
|
|
}
|
|
break;
|
|
case WKBD_RAISE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && wwin->frame) {
|
|
CloseWindowMenu(scr);
|
|
wRaiseFrame(wwin->frame->core);
|
|
}
|
|
break;
|
|
case WKBD_LOWER:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && wwin->frame) {
|
|
CloseWindowMenu(scr);
|
|
wLowerFrame(wwin->frame->core);
|
|
}
|
|
break;
|
|
case WKBD_CLOSE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
if (wwin->protocols.DELETE_WINDOW && !WFLAGP(wwin, kill_close))
|
|
wClientSendProtocol(wwin, w_global.atom.wm.delete_window,
|
|
w_global.timestamp.last_event);
|
|
}
|
|
break;
|
|
case WKBD_FOCUSNEXT:
|
|
wWindowFocusNext(wwin, True);
|
|
break;
|
|
case WKBD_FOCUSPREV:
|
|
wWindowFocusPrev(wwin, True);
|
|
break;
|
|
case WKBD_WORKSPACE1: case WKBD_WORKSPACE2: case WKBD_WORKSPACE3:
|
|
case WKBD_WORKSPACE4: case WKBD_WORKSPACE5: case WKBD_WORKSPACE6:
|
|
case WKBD_WORKSPACE7: case WKBD_WORKSPACE8: case WKBD_WORKSPACE9:
|
|
case WKBD_WORKSPACE10:
|
|
{
|
|
int widx2 = command - WKBD_WORKSPACE1;
|
|
int ws = (scr->current_workspace / 10) * 10 + widx2;
|
|
if (wPreferences.ws_advance || ws < scr->workspace_count)
|
|
wWorkspaceChange(scr, ws);
|
|
}
|
|
break;
|
|
case WKBD_NEXTWORKSPACE:
|
|
wWorkspaceRelativeChange(scr, 1);
|
|
break;
|
|
case WKBD_PREVWORKSPACE:
|
|
wWorkspaceRelativeChange(scr, -1);
|
|
break;
|
|
default:
|
|
/* Complex shortcuts that need XEvent (pointer coords, menus) are
|
|
* not dispatched here; return 0 so the caller forwards the key. */
|
|
return 0;
|
|
}
|
|
|
|
return 1; /* consumed */
|
|
}
|
|
|
|
static void handleKeyPress(WMEvent * event)
|
|
{
|
|
WScreen *scr = wScreenForRootWindow(event->window);
|
|
WWindow *wwin = scr->focused_window;
|
|
short i, widx;
|
|
int modifiers;
|
|
int command = -1;
|
|
|
|
/* ignore CapsLock */
|
|
modifiers = event->u.key.state & w_global.shortcut.modifiers_mask;
|
|
|
|
for (i = 0; i < WKBD_LAST; i++) {
|
|
if (wKeyBindings[i].keycode == 0)
|
|
continue;
|
|
|
|
if (wKeyBindings[i].keycode == event->u.key.keycode && ( /*wKeyBindings[i].modifier==0
|
|
|| */ wKeyBindings[i].modifier ==
|
|
modifiers)) {
|
|
command = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (command < 0) {
|
|
|
|
if (!wRootMenuPerformShortcut(event)) {
|
|
static int dontLoop = 0;
|
|
|
|
if (dontLoop > 10) {
|
|
wwarning("problem with key event processing code");
|
|
return;
|
|
}
|
|
dontLoop++;
|
|
/* if the focused window is an internal window, try redispatching
|
|
* the event to the managed window, as it can be a WINGs window */
|
|
if (wwin && wwin->flags.internal_window && wwin->client_leader != WM_NATIVE_NONE) {
|
|
/* client_leader contains the WINGs toplevel */
|
|
event->window = wwin->client_leader;
|
|
WMHandleEvent(event);
|
|
}
|
|
dontLoop--;
|
|
}
|
|
return;
|
|
}
|
|
#define ISMAPPED(w) ((w) && !(w)->flags.miniaturized && ((w)->flags.mapped || (w)->flags.shaded))
|
|
#define ISFOCUSED(w) ((w) && (w)->flags.focused)
|
|
|
|
switch (command) {
|
|
|
|
case WKBD_ROOTMENU:
|
|
/*OpenRootMenu(scr, event->u.key.x_root, event->u.key.y_root, True); */
|
|
if (!CheckFullScreenWindowFocused(scr)) {
|
|
WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
|
|
OpenRootMenu(scr, rect.pos.x + rect.size.width / 2, rect.pos.y + rect.size.height / 2,
|
|
True);
|
|
}
|
|
break;
|
|
case WKBD_WINDOWLIST:
|
|
if (!CheckFullScreenWindowFocused(scr)) {
|
|
WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
|
|
OpenSwitchMenu(scr, rect.pos.x + rect.size.width / 2, rect.pos.y + rect.size.height / 2,
|
|
True);
|
|
}
|
|
break;
|
|
|
|
case WKBD_WINDOWMENU:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin))
|
|
if (wwin->frame)
|
|
OpenWindowMenu(wwin, wwin->frame_x, wwin->frame_y + wwin->frame->top_width, True);
|
|
break;
|
|
case WKBD_MINIMIZEALL:
|
|
CloseWindowMenu(scr);
|
|
wHideAll(scr);
|
|
break;
|
|
case WKBD_MINIATURIZE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin)
|
|
&& !WFLAGP(wwin, no_miniaturizable)) {
|
|
CloseWindowMenu(scr);
|
|
|
|
if (wwin->protocols.MINIATURIZE_WINDOW)
|
|
wClientSendProtocol(wwin, w_global.atom.gnustep.wm_miniaturize_window, event->u.key.time);
|
|
else {
|
|
wIconifyWindow(wwin);
|
|
}
|
|
}
|
|
break;
|
|
case WKBD_HIDE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
|
|
WApplication *wapp = wApplicationOf(wwin->main_window);
|
|
CloseWindowMenu(scr);
|
|
|
|
if (wapp && !WFLAGP(wapp->main_window_desc, no_appicon)) {
|
|
wHideApplication(wapp);
|
|
}
|
|
}
|
|
break;
|
|
case WKBD_HIDE_OTHERS:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
|
|
wHideOtherApplications(wwin);
|
|
}
|
|
break;
|
|
case WKBD_MAXIMIZE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
|
|
handleMaximize(wwin, MAX_VERTICAL | MAX_HORIZONTAL | MAX_KEYBOARD);
|
|
}
|
|
break;
|
|
case WKBD_VMAXIMIZE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
|
|
handleMaximize(wwin, MAX_VERTICAL | MAX_KEYBOARD);
|
|
}
|
|
break;
|
|
case WKBD_HMAXIMIZE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
|
|
handleMaximize(wwin, MAX_HORIZONTAL | MAX_KEYBOARD);
|
|
movePointerToWindowCenter(wwin);
|
|
}
|
|
break;
|
|
case WKBD_CENTRAL:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
|
|
handleMaximize(wwin, MAX_CENTRAL | MAX_KEYBOARD);
|
|
movePointerToWindowCenter(wwin);
|
|
}
|
|
break;
|
|
case WKBD_LHMAXIMIZE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
|
|
handleMaximize(wwin, MAX_VERTICAL | MAX_LEFTHALF | MAX_KEYBOARD);
|
|
movePointerToWindowCenter(wwin);
|
|
}
|
|
break;
|
|
case WKBD_RHMAXIMIZE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
|
|
handleMaximize(wwin, MAX_VERTICAL | MAX_RIGHTHALF | MAX_KEYBOARD);
|
|
movePointerToWindowCenter(wwin);
|
|
}
|
|
break;
|
|
case WKBD_THMAXIMIZE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
|
|
handleMaximize(wwin, MAX_HORIZONTAL | MAX_TOPHALF | MAX_KEYBOARD);
|
|
movePointerToWindowCenter(wwin);
|
|
}
|
|
break;
|
|
case WKBD_BHMAXIMIZE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
|
|
handleMaximize(wwin, MAX_HORIZONTAL | MAX_BOTTOMHALF | MAX_KEYBOARD);
|
|
movePointerToWindowCenter(wwin);
|
|
}
|
|
break;
|
|
case WKBD_LTCMAXIMIZE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
|
|
handleMaximize(wwin, MAX_LEFTHALF | MAX_TOPHALF | MAX_KEYBOARD);
|
|
movePointerToWindowCenter(wwin);
|
|
}
|
|
break;
|
|
case WKBD_RTCMAXIMIZE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
|
|
handleMaximize(wwin, MAX_RIGHTHALF | MAX_TOPHALF | MAX_KEYBOARD);
|
|
movePointerToWindowCenter(wwin);
|
|
}
|
|
break;
|
|
case WKBD_LBCMAXIMIZE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
|
|
handleMaximize(wwin, MAX_LEFTHALF | MAX_BOTTOMHALF | MAX_KEYBOARD);
|
|
movePointerToWindowCenter(wwin);
|
|
}
|
|
break;
|
|
case WKBD_RBCMAXIMIZE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
|
|
handleMaximize(wwin, MAX_RIGHTHALF | MAX_BOTTOMHALF | MAX_KEYBOARD);
|
|
movePointerToWindowCenter(wwin);
|
|
}
|
|
break;
|
|
case WKBD_MAXIMUS:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
|
|
handleMaximize(wwin, MAX_MAXIMUS | MAX_KEYBOARD);
|
|
}
|
|
break;
|
|
case WKBD_KEEP_ON_TOP:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && wwin->frame) {
|
|
CloseWindowMenu(scr);
|
|
|
|
if (wwin->frame->core->stacking->window_level != WMFloatingLevel)
|
|
ChangeStackingLevel(wwin->frame->core, WMFloatingLevel);
|
|
else
|
|
ChangeStackingLevel(wwin->frame->core, WMNormalLevel);
|
|
}
|
|
break;
|
|
|
|
case WKBD_KEEP_AT_BOTTOM:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && wwin->frame) {
|
|
CloseWindowMenu(scr);
|
|
|
|
if (wwin->frame->core->stacking->window_level != WMSunkenLevel)
|
|
ChangeStackingLevel(wwin->frame->core, WMSunkenLevel);
|
|
else
|
|
ChangeStackingLevel(wwin->frame->core, WMNormalLevel);
|
|
}
|
|
break;
|
|
case WKBD_OMNIPRESENT:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
|
|
CloseWindowMenu(scr);
|
|
|
|
wWindowSetOmnipresent(wwin, !wwin->flags.omnipresent);
|
|
}
|
|
break;
|
|
case WKBD_RAISE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && wwin->frame) {
|
|
CloseWindowMenu(scr);
|
|
|
|
wRaiseFrame(wwin->frame->core);
|
|
}
|
|
break;
|
|
case WKBD_LOWER:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && wwin->frame) {
|
|
CloseWindowMenu(scr);
|
|
|
|
wLowerFrame(wwin->frame->core);
|
|
}
|
|
break;
|
|
case WKBD_RAISELOWER:
|
|
/* raise or lower the window under the pointer, not the
|
|
* focused one
|
|
*/
|
|
wwin = windowUnderPointer(scr);
|
|
if (wwin && wwin->frame)
|
|
wRaiseLowerFrame(wwin->frame->core);
|
|
break;
|
|
case WKBD_SHADE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && !WFLAGP(wwin, no_shadeable)) {
|
|
if (wwin->flags.shaded)
|
|
wUnshadeWindow(wwin);
|
|
else
|
|
wShadeWindow(wwin);
|
|
}
|
|
break;
|
|
case WKBD_MOVERESIZE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && (IS_RESIZABLE(wwin) || IS_MOVABLE(wwin))) {
|
|
CloseWindowMenu(scr);
|
|
|
|
wKeyboardMoveResizeWindow(wwin);
|
|
}
|
|
break;
|
|
case WKBD_CLOSE:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin) && !WFLAGP(wwin, no_closable)) {
|
|
CloseWindowMenu(scr);
|
|
if (wwin->protocols.DELETE_WINDOW)
|
|
wClientSendProtocol(wwin, w_global.atom.wm.delete_window, event->u.key.time);
|
|
}
|
|
break;
|
|
case WKBD_SELECT:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
|
|
wSelectWindow(wwin, !wwin->flags.selected);
|
|
}
|
|
break;
|
|
|
|
case WKBD_WORKSPACEMAP:
|
|
if (wPreferences.enable_workspace_pager)
|
|
StartWorkspaceMap(scr);
|
|
break;
|
|
|
|
case WKBD_FOCUSNEXT:
|
|
StartWindozeCycle(wwin, event, True, False);
|
|
break;
|
|
|
|
case WKBD_FOCUSPREV:
|
|
StartWindozeCycle(wwin, event, False, False);
|
|
break;
|
|
|
|
case WKBD_GROUPNEXT:
|
|
StartWindozeCycle(wwin, event, True, True);
|
|
break;
|
|
|
|
case WKBD_GROUPPREV:
|
|
StartWindozeCycle(wwin, event, False, True);
|
|
break;
|
|
|
|
case WKBD_WORKSPACE1 ... WKBD_WORKSPACE10:
|
|
widx = command - WKBD_WORKSPACE1;
|
|
i = (scr->current_workspace / 10) * 10 + widx;
|
|
if (wPreferences.ws_advance || i < scr->workspace_count)
|
|
wWorkspaceChange(scr, i);
|
|
break;
|
|
|
|
case WKBD_NEXTWORKSPACE:
|
|
wWorkspaceRelativeChange(scr, 1);
|
|
break;
|
|
case WKBD_PREVWORKSPACE:
|
|
wWorkspaceRelativeChange(scr, -1);
|
|
break;
|
|
case WKBD_LASTWORKSPACE:
|
|
wWorkspaceChange(scr, scr->last_workspace);
|
|
break;
|
|
|
|
case WKBD_MOVE_WORKSPACE1 ... WKBD_MOVE_WORKSPACE10:
|
|
widx = command - WKBD_MOVE_WORKSPACE1;
|
|
i = (scr->current_workspace / 10) * 10 + widx;
|
|
if (wwin && (wPreferences.ws_advance || i < scr->workspace_count))
|
|
wWindowChangeWorkspace(wwin, i);
|
|
break;
|
|
|
|
case WKBD_MOVE_NEXTWORKSPACE:
|
|
if (wwin)
|
|
wWindowChangeWorkspaceRelative(wwin, 1);
|
|
break;
|
|
case WKBD_MOVE_PREVWORKSPACE:
|
|
if (wwin)
|
|
wWindowChangeWorkspaceRelative(wwin, -1);
|
|
break;
|
|
case WKBD_MOVE_LASTWORKSPACE:
|
|
if (wwin)
|
|
wWindowChangeWorkspace(wwin, scr->last_workspace);
|
|
break;
|
|
|
|
case WKBD_MOVE_NEXTWSLAYER:
|
|
case WKBD_MOVE_PREVWSLAYER:
|
|
{
|
|
if (wwin) {
|
|
int row, column;
|
|
|
|
row = scr->current_workspace / 10;
|
|
column = scr->current_workspace % 10;
|
|
|
|
if (command == WKBD_MOVE_NEXTWSLAYER) {
|
|
if ((row + 1) * 10 < scr->workspace_count)
|
|
wWindowChangeWorkspace(wwin, column + (row + 1) * 10);
|
|
} else {
|
|
if (row > 0)
|
|
wWindowChangeWorkspace(wwin, column + (row - 1) * 10);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WKBD_WINDOW1:
|
|
case WKBD_WINDOW2:
|
|
case WKBD_WINDOW3:
|
|
case WKBD_WINDOW4:
|
|
case WKBD_WINDOW5:
|
|
case WKBD_WINDOW6:
|
|
case WKBD_WINDOW7:
|
|
case WKBD_WINDOW8:
|
|
case WKBD_WINDOW9:
|
|
case WKBD_WINDOW10:
|
|
|
|
widx = command - WKBD_WINDOW1;
|
|
|
|
if (scr->shortcutWindows[widx]) {
|
|
WMArray *list = scr->shortcutWindows[widx];
|
|
int cw;
|
|
int count = WMGetArrayItemCount(list);
|
|
WWindow *twin;
|
|
WMArrayIterator iter;
|
|
WWindow *wwin;
|
|
|
|
wUnselectWindows(scr);
|
|
cw = scr->current_workspace;
|
|
|
|
WM_ETARETI_ARRAY(list, wwin, iter) {
|
|
if (count > 1)
|
|
wWindowChangeWorkspace(wwin, cw);
|
|
|
|
wMakeWindowVisible(wwin);
|
|
|
|
if (count > 1)
|
|
wSelectWindow(wwin, True);
|
|
}
|
|
|
|
/* rotate the order of windows, to create a cycling effect */
|
|
twin = WMGetFromArray(list, 0);
|
|
WMDeleteFromArray(list, 0);
|
|
WMAddToArray(list, twin);
|
|
|
|
} else if (wwin && ISMAPPED(wwin) && ISFOCUSED(wwin)) {
|
|
if (scr->shortcutWindows[widx]) {
|
|
WMFreeArray(scr->shortcutWindows[widx]);
|
|
scr->shortcutWindows[widx] = NULL;
|
|
}
|
|
|
|
if (wwin->flags.selected && scr->selected_windows) {
|
|
scr->shortcutWindows[widx] = WMDuplicateArray(scr->selected_windows);
|
|
/*WMRemoveFromArray(scr->shortcutWindows[index], wwin);
|
|
WMInsertInArray(scr->shortcutWindows[index], 0, wwin); */
|
|
} else {
|
|
scr->shortcutWindows[widx] = WMCreateArray(4);
|
|
WMAddToArray(scr->shortcutWindows[widx], wwin);
|
|
}
|
|
|
|
wSelectWindow(wwin, !wwin->flags.selected);
|
|
wm_backend->event_flush();
|
|
wusleep(3000);
|
|
wSelectWindow(wwin, !wwin->flags.selected);
|
|
wm_backend->event_flush();
|
|
|
|
} else if (scr->selected_windows && WMGetArrayItemCount(scr->selected_windows)) {
|
|
|
|
if (wwin->flags.selected && scr->selected_windows) {
|
|
if (scr->shortcutWindows[widx]) {
|
|
WMFreeArray(scr->shortcutWindows[widx]);
|
|
}
|
|
scr->shortcutWindows[widx] = WMDuplicateArray(scr->selected_windows);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case WKBD_MOVE_12_TO_6_HEAD:
|
|
case WKBD_MOVE_6_TO_12_HEAD:
|
|
if (wwin)
|
|
moveBetweenHeads(wwin, command - WKBD_MOVE_12_TO_6_HEAD);
|
|
|
|
break;
|
|
|
|
case WKBD_RELAUNCH:
|
|
if (ISMAPPED(wwin) && ISFOCUSED(wwin))
|
|
(void) RelaunchWindow(wwin);
|
|
|
|
break;
|
|
|
|
case WKBD_SWITCH_SCREEN:
|
|
if (w_global.screen_count > 1) {
|
|
WScreen *scr2;
|
|
int i;
|
|
|
|
/* find index of this screen */
|
|
for (i = 0; i < w_global.screen_count; i++) {
|
|
if (wScreenWithNumber(i) == scr)
|
|
break;
|
|
}
|
|
i++;
|
|
if (i >= w_global.screen_count) {
|
|
i = 0;
|
|
}
|
|
scr2 = wScreenWithNumber(i);
|
|
|
|
if (scr2) {
|
|
wm_backend->pointer_warp_to(
|
|
scr2->scr_width / 2, scr2->scr_height / 2);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WKBD_RUN:
|
|
{
|
|
ExecuteInputCommand(scr, _("exec %A(Run, Type command:)"));
|
|
break;
|
|
}
|
|
|
|
case WKBD_EXIT:
|
|
{
|
|
/* quick mode is not allowed to prevent inadvertently call */
|
|
ExecuteExitCommand(scr, 0);
|
|
break;
|
|
}
|
|
|
|
case WKBD_PRINTS:
|
|
{
|
|
ScreenCapture(scr, PRINT_SCREEN);
|
|
break;
|
|
}
|
|
|
|
case WKBD_PRINTW:
|
|
{
|
|
ScreenCapture(scr, PRINT_WINDOW);
|
|
break;
|
|
}
|
|
|
|
case WKBD_PRINTP:
|
|
{
|
|
ScreenCapture(scr, PRINT_PARTIAL);
|
|
break;
|
|
}
|
|
|
|
case WKBD_NEXTWSLAYER:
|
|
case WKBD_PREVWSLAYER:
|
|
{
|
|
int row, column;
|
|
|
|
row = scr->current_workspace / 10;
|
|
column = scr->current_workspace % 10;
|
|
|
|
if (command == WKBD_NEXTWSLAYER) {
|
|
if ((row + 1) * 10 < scr->workspace_count)
|
|
wWorkspaceChange(scr, column + (row + 1) * 10);
|
|
} else {
|
|
if (row > 0)
|
|
wWorkspaceChange(scr, column + (row - 1) * 10);
|
|
}
|
|
}
|
|
break;
|
|
case WKBD_CLIPRAISELOWER:
|
|
if (!wPreferences.flags.noclip)
|
|
wDockRaiseLower(scr->workspaces[scr->current_workspace]->clip);
|
|
break;
|
|
case WKBD_DOCKRAISELOWER:
|
|
if (!wPreferences.flags.nodock)
|
|
wDockRaiseLower(scr->dock);
|
|
break;
|
|
#ifdef KEEP_XKB_LOCK_STATUS
|
|
case WKBD_TOGGLE:
|
|
if (wPreferences.modelock) {
|
|
/*toggle */
|
|
wwin = scr->focused_window;
|
|
|
|
if (wwin && wwin->flags.mapped && wwin->frame
|
|
&& WIN_WORKSPACE(wwin) == wwin->screen_ptr->current_workspace
|
|
&& !wwin->flags.miniaturized && !wwin->flags.hidden) {
|
|
int cur_group = wm_backend->xkb_get_group();
|
|
|
|
wwin->frame->languagemode = wwin->frame->last_languagemode;
|
|
wwin->frame->last_languagemode = cur_group;
|
|
wm_backend->xkb_lock_group(wwin->frame->languagemode);
|
|
|
|
}
|
|
}
|
|
break;
|
|
#endif /* KEEP_XKB_LOCK_STATUS */
|
|
}
|
|
}
|
|
|
|
#define CORNER_NONE 0
|
|
#define CORNER_TOPLEFT 1
|
|
#define CORNER_TOPRIGHT 2
|
|
#define CORNER_BOTTOMLEFT 3
|
|
#define CORNER_BOTTOMRIGHT 4
|
|
|
|
static int get_corner(WMRect rect, WMPoint p)
|
|
{
|
|
if (p.x <= (rect.pos.x + wPreferences.hot_corner_edge) && p.y <= (rect.pos.y + wPreferences.hot_corner_edge))
|
|
return CORNER_TOPLEFT;
|
|
if (p.x >= (rect.pos.x + rect.size.width - wPreferences.hot_corner_edge) && p.y <= (rect.pos.y + wPreferences.hot_corner_edge))
|
|
return CORNER_TOPRIGHT;
|
|
if (p.x <= (rect.pos.x + wPreferences.hot_corner_edge) && p.y >= (rect.pos.y + rect.size.height - wPreferences.hot_corner_edge))
|
|
return CORNER_BOTTOMLEFT;
|
|
if (p.x >= (rect.pos.x + rect.size.width - wPreferences.hot_corner_edge) && p.y >= (rect.pos.y + rect.size.height - wPreferences.hot_corner_edge))
|
|
return CORNER_BOTTOMRIGHT;
|
|
return CORNER_NONE;
|
|
}
|
|
|
|
static void hotCornerDelay(void *data)
|
|
{
|
|
WScreen *scr = (WScreen *) data;
|
|
if (scr->flags.in_hot_corner && wPreferences.hot_corner_actions[scr->flags.in_hot_corner - 1])
|
|
ExecuteShellCommand(scr, wPreferences.hot_corner_actions[scr->flags.in_hot_corner - 1]);
|
|
WMDeleteTimerHandler(scr->hot_corner_timer);
|
|
scr->hot_corner_timer = NULL;
|
|
}
|
|
|
|
static void handleMotionNotify(WMEvent *event)
|
|
{
|
|
if (wPreferences.scrollable_menus || wPreferences.hot_corners) {
|
|
WScreen *scr = wScreenForRootWindow(event->window);
|
|
WMPoint p = wmkpoint(event->u.motion.x_root, event->u.motion.y_root);
|
|
WMRect rect = wGetRectForHead(scr, wGetHeadForPoint(scr, p));
|
|
|
|
if (wPreferences.hot_corners) {
|
|
if (!scr->flags.in_hot_corner) {
|
|
scr->flags.in_hot_corner = get_corner(rect, p);
|
|
if (scr->flags.in_hot_corner && !scr->hot_corner_timer)
|
|
scr->hot_corner_timer = WMAddTimerHandler(wPreferences.hot_corner_delay, hotCornerDelay, scr);
|
|
} else {
|
|
int out_hot_corner = 0;
|
|
|
|
switch (scr->flags.in_hot_corner) {
|
|
case CORNER_TOPLEFT:
|
|
if ((p.x > (rect.pos.x + wPreferences.hot_corner_edge)) ||
|
|
(p.y > (rect.pos.y + wPreferences.hot_corner_edge)))
|
|
out_hot_corner = 1;
|
|
break;
|
|
case CORNER_TOPRIGHT:
|
|
if ((p.x < (rect.pos.x + rect.size.width - wPreferences.hot_corner_edge)) ||
|
|
(p.y > (rect.pos.y + wPreferences.hot_corner_edge)))
|
|
out_hot_corner = 1;
|
|
break;
|
|
case CORNER_BOTTOMLEFT:
|
|
if ((p.x > (rect.pos.x + wPreferences.hot_corner_edge)) ||
|
|
(p.y < (rect.pos.y + rect.size.height - wPreferences.hot_corner_edge)))
|
|
out_hot_corner = 1;
|
|
break;
|
|
case CORNER_BOTTOMRIGHT:
|
|
if ((p.x < (rect.pos.x + rect.size.width - wPreferences.hot_corner_edge)) ||
|
|
(p.y < (rect.pos.y + rect.size.height - wPreferences.hot_corner_edge)))
|
|
out_hot_corner = 1;
|
|
break;
|
|
|
|
}
|
|
if (out_hot_corner) {
|
|
scr->flags.in_hot_corner = CORNER_NONE;
|
|
if (scr->hot_corner_timer) {
|
|
WMDeleteTimerHandler(scr->hot_corner_timer);
|
|
scr->hot_corner_timer = NULL;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (wPreferences.scrollable_menus) {
|
|
if (scr->flags.jump_back_pending ||
|
|
p.x <= (rect.pos.x + 1) ||
|
|
p.x >= (rect.pos.x + rect.size.width - 2) ||
|
|
p.y <= (rect.pos.y + 1) || p.y >= (rect.pos.y + rect.size.height - 2)) {
|
|
WMenu *menu;
|
|
|
|
menu = wMenuUnderPointer(scr);
|
|
if (menu != NULL)
|
|
wMenuScroll(menu);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#undef CORNER_NONE
|
|
#undef CORNER_TOPLEFT
|
|
#undef CORNER_TOPRIGHT
|
|
#undef CORNER_BOTTOMLEFT
|
|
#undef CORNER_BOTTOMRIGHT
|
|
|
|
static void handleVisibilityNotify(WMEvent * event)
|
|
{
|
|
WWindow *wwin;
|
|
|
|
wwin = wWindowFor(event->u.visibility.window);
|
|
if (!wwin)
|
|
return;
|
|
wwin->flags.obscured = (event->u.visibility.state == WM_VISIBILITY_FULLY_OBSCURED);
|
|
}
|
|
|
|
/* Selection request/clear handling moved to backend
|
|
* (X11 backend: x11_handle_selection_request/clear; Wayland: no-op) */
|
|
|