windowmaker-wl/src/event.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) */