ewmh.c revision b18c2d1e
10bbfda8aSnia/* 20bbfda8aSnia * Copyright 2014 Olaf Seibert 30bbfda8aSnia */ 40bbfda8aSnia 50bbfda8aSnia/* 60bbfda8aSnia * Implements some of the Extended Window Manager Hints, as (extremely 70bbfda8aSnia * poorly) documented at 80bbfda8aSnia * http://standards.freedesktop.org/wm-spec/wm-spec-1.3.html . 90bbfda8aSnia * In fact, the wiki page that refers to that as being the current version 100bbfda8aSnia * (http://www.freedesktop.org/wiki/Specifications/wm-spec/) 110bbfda8aSnia * neglects to tell us there are newer versions 1.4 and 1.5 at 120bbfda8aSnia * http://standards.freedesktop.org/wm-spec/wm-spec-1.5.html 130bbfda8aSnia * (which has a listable directory that I discovered by accident). 140bbfda8aSnia * The same wiki page also has lots of dead links to a CVS repository. 150bbfda8aSnia * Nevertheless, it is where one ends up if one starts at 160bbfda8aSnia * http://www.freedesktop.org/wiki/Specifications/ . 170bbfda8aSnia * 180bbfda8aSnia * EWMH is an extension to the ICCCM (Inter-Client Communication 190bbfda8aSnia * Conventions Manual). 200bbfda8aSnia * http://tronche.com/gui/x/icccm/ 210bbfda8aSnia * 220bbfda8aSnia * To fill in lots of details, the source code of other window managers 230bbfda8aSnia * has been consulted. 240bbfda8aSnia */ 250bbfda8aSnia 260bbfda8aSnia#include "ctwm.h" 270bbfda8aSnia 280bbfda8aSnia#include <stdio.h> 290bbfda8aSnia#include <stdlib.h> 300bbfda8aSnia#include <time.h> 310bbfda8aSnia#include <stdint.h> 320bbfda8aSnia#include <assert.h> 330bbfda8aSnia 340bbfda8aSnia#include <X11/Xatom.h> 350bbfda8aSnia#include <X11/extensions/shape.h> 360bbfda8aSnia 370bbfda8aSnia#include "ctwm_atoms.h" 38b18c2d1eSnia#include "ctwm_shutdown.h" 390bbfda8aSnia#include "ewmh_atoms.h" 400bbfda8aSnia#include "screen.h" 410bbfda8aSnia#include "events.h" 420bbfda8aSnia#include "event_handlers.h" 430bbfda8aSnia#include "functions_defs.h" 440bbfda8aSnia#include "icons.h" 450bbfda8aSnia#include "otp.h" 460bbfda8aSnia#include "image.h" 470bbfda8aSnia#include "list.h" 480bbfda8aSnia#include "functions.h" 490bbfda8aSnia#include "occupation.h" 50b18c2d1eSnia#include "r_layout.h" 51b18c2d1eSnia#include "util.h" 520bbfda8aSnia#include "vscreen.h" 530bbfda8aSnia#include "win_iconify.h" 540bbfda8aSnia#include "win_ops.h" 550bbfda8aSnia#include "win_resize.h" 560bbfda8aSnia#include "win_utils.h" 570bbfda8aSnia#include "workspace_utils.h" 580bbfda8aSnia 590bbfda8aSnia/* #define DEBUG_EWMH */ 600bbfda8aSnia 610bbfda8aSniaAtom XEWMHAtom[NUM_EWMH_XATOMS]; 620bbfda8aSnia 630bbfda8aSnia#define NET_WM_STATE_REMOVE 0 /* remove/unset property */ 640bbfda8aSnia#define NET_WM_STATE_ADD 1 /* add/set property */ 650bbfda8aSnia#define NET_WM_STATE_TOGGLE 2 /* toggle property */ 660bbfda8aSnia 670bbfda8aSnia#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 680bbfda8aSnia#define _NET_WM_MOVERESIZE_SIZE_TOP 1 690bbfda8aSnia#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 700bbfda8aSnia#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 710bbfda8aSnia#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 720bbfda8aSnia#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 730bbfda8aSnia#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 740bbfda8aSnia#define _NET_WM_MOVERESIZE_SIZE_LEFT 7 750bbfda8aSnia#define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */ 760bbfda8aSnia#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */ 770bbfda8aSnia#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */ 780bbfda8aSnia#define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */ 790bbfda8aSnia 800bbfda8aSniastatic Image *ExtractIcon(ScreenInfo *scr, unsigned long *prop, int width, 810bbfda8aSnia int height); 820bbfda8aSniastatic void EwmhClientMessage_NET_WM_DESKTOP(XClientMessageEvent *msg); 830bbfda8aSniastatic void EwmhClientMessage_NET_WM_STATE(XClientMessageEvent *msg); 840bbfda8aSniastatic void EwmhClientMessage_NET_ACTIVE_WINDOW(XClientMessageEvent *msg); 850bbfda8aSniastatic void EwmhClientMessage_NET_WM_MOVERESIZE(XClientMessageEvent *msg); 86b18c2d1eSniastatic void EwmhClientMessage_NET_CLOSE_WINDOW(XClientMessageEvent *msg); 870bbfda8aSniastatic XEvent synth_btnevent_for_moveresize(TwmWindow *twm_win); 880bbfda8aSniastatic unsigned long EwmhGetWindowProperty(Window w, Atom name, Atom type); 890bbfda8aSniastatic void EwmhGetStrut(TwmWindow *twm_win, bool update); 900bbfda8aSniastatic void EwmhRemoveStrut(TwmWindow *twm_win); 910bbfda8aSniastatic void EwmhSet_NET_WORKAREA(ScreenInfo *scr); 920bbfda8aSniastatic int EwmhGet_NET_WM_STATE(TwmWindow *twm_win); 930bbfda8aSniastatic void EwmhClientMessage_NET_WM_STATEchange(TwmWindow *twm_win, int change, 940bbfda8aSnia int newVal); 950bbfda8aSnia 960bbfda8aSnia#define ALL_WORKSPACES 0xFFFFFFFFU 970bbfda8aSnia 980bbfda8aSniastatic void SendPropertyMessage(Window to, Window about, 990bbfda8aSnia Atom messagetype, 1000bbfda8aSnia long l0, long l1, long l2, long l3, long l4, 1010bbfda8aSnia long mask) 1020bbfda8aSnia{ 1030bbfda8aSnia XEvent e; 1040bbfda8aSnia 1050bbfda8aSnia e.xclient.type = ClientMessage; 1060bbfda8aSnia e.xclient.message_type = messagetype; 1070bbfda8aSnia e.xclient.display = dpy; 1080bbfda8aSnia e.xclient.window = about; 1090bbfda8aSnia e.xclient.format = 32; 1100bbfda8aSnia e.xclient.data.l[0] = l0; 1110bbfda8aSnia e.xclient.data.l[1] = l1; 1120bbfda8aSnia e.xclient.data.l[2] = l2; 1130bbfda8aSnia e.xclient.data.l[3] = l3; 1140bbfda8aSnia e.xclient.data.l[4] = l4; 1150bbfda8aSnia 1160bbfda8aSnia XSendEvent(dpy, to, False, mask, &e); 1170bbfda8aSnia} 1180bbfda8aSnia 1190bbfda8aSniastatic void EwmhInitAtoms(void) 1200bbfda8aSnia{ 1210bbfda8aSnia XInternAtoms(dpy, XEWMHAtomNames, NUM_EWMH_XATOMS, False, XEWMHAtom); 1220bbfda8aSnia} 1230bbfda8aSnia 1240bbfda8aSniastatic bool caughtError; 1250bbfda8aSnia 1260bbfda8aSniastatic int CatchError(Display *display, XErrorEvent *event) 1270bbfda8aSnia{ 1280bbfda8aSnia caughtError = true; 1290bbfda8aSnia return 0; 1300bbfda8aSnia} 1310bbfda8aSnia 1320bbfda8aSniavoid EwmhInit(void) 1330bbfda8aSnia{ 1340bbfda8aSnia EwmhInitAtoms(); 1350bbfda8aSnia} 1360bbfda8aSnia 1370bbfda8aSnia/* 1380bbfda8aSnia * Force-generate some event, so that we know the current time. 1390bbfda8aSnia * 1400bbfda8aSnia * Suggested in the ICCCM: 1410bbfda8aSnia * http://tronche.com/gui/x/icccm/sec-2.html#s-2.1 1420bbfda8aSnia */ 1430bbfda8aSnia 1440bbfda8aSniastatic void GenerateTimestamp(ScreenInfo *scr) 1450bbfda8aSnia{ 1460bbfda8aSnia XEvent event; 1470bbfda8aSnia struct timespec tosleep; 1480bbfda8aSnia int timeout = 200; /* 0.2 seconds in ms */ 1490bbfda8aSnia int found; 1500bbfda8aSnia 1510bbfda8aSnia /* Sleep in 10ms chunks */ 1520bbfda8aSnia tosleep.tv_sec = 0; 1530bbfda8aSnia tosleep.tv_nsec = (10 * 1000 * 1000); 1540bbfda8aSnia 1550bbfda8aSnia if(EventTime > 0) { 1560bbfda8aSnia return; 1570bbfda8aSnia } 1580bbfda8aSnia 1590bbfda8aSnia XChangeProperty(dpy, scr->icccm_Window, 1600bbfda8aSnia XA_WM_CLASS, XA_STRING, 1610bbfda8aSnia 8, PropModeAppend, NULL, 0); 1620bbfda8aSnia 1630bbfda8aSnia while(timeout > 0) { 1640bbfda8aSnia found = XCheckTypedWindowEvent(dpy, scr->icccm_Window, PropertyNotify, &event); 1650bbfda8aSnia if(found) { 1660bbfda8aSnia break; 1670bbfda8aSnia } 1680bbfda8aSnia nanosleep(&tosleep, NULL); 1690bbfda8aSnia timeout -= 10; 1700bbfda8aSnia } 1710bbfda8aSnia 1720bbfda8aSnia if(found) { 1730bbfda8aSnia#ifdef DEBUG_EWMH 1740bbfda8aSnia fprintf(stderr, "GenerateTimestamp: time = %ld, timeout left = %d\n", 1750bbfda8aSnia event.xproperty.time, timeout); 1760bbfda8aSnia#endif /* DEBUG_EWMH */ 1770bbfda8aSnia if(EventTime < event.xproperty.time) { 1780bbfda8aSnia EventTime = event.xproperty.time; 1790bbfda8aSnia } 1800bbfda8aSnia } 1810bbfda8aSnia} 1820bbfda8aSnia 1830bbfda8aSnia/* 1840bbfda8aSnia * Perform the "replace the window manager" protocol, as vaguely hinted 1850bbfda8aSnia * at by the ICCCM section 4.3. 1860bbfda8aSnia * http://tronche.com/gui/x/icccm/sec-4.html#s-4.3 1870bbfda8aSnia * 1880bbfda8aSnia * We do want to run through this even if we're not running with 1890bbfda8aSnia * --replace ourselves, because it also sets up the bits for other 1900bbfda8aSnia * invocations to --replace us. 1910bbfda8aSnia * 1920bbfda8aSnia * TODO: convert the selection to atom VERSION. 1930bbfda8aSnia */ 1940bbfda8aSniastatic bool EwmhReplaceWM(ScreenInfo *scr) 1950bbfda8aSnia{ 1960bbfda8aSnia char atomname[32]; 1970bbfda8aSnia Atom wmAtom; 1980bbfda8aSnia Window selectionOwner; 1990bbfda8aSnia 2000bbfda8aSnia snprintf(atomname, sizeof(atomname), "WM_S%d", scr->screen); 2010bbfda8aSnia wmAtom = XInternAtom(dpy, atomname, False); 2020bbfda8aSnia 2030bbfda8aSnia selectionOwner = XGetSelectionOwner(dpy, wmAtom); 2040bbfda8aSnia if(selectionOwner == scr->icccm_Window) { 2050bbfda8aSnia selectionOwner = None; 2060bbfda8aSnia } 2070bbfda8aSnia 2080bbfda8aSnia if(selectionOwner != None) { 2090bbfda8aSnia XErrorHandler oldHandler; 2100bbfda8aSnia 2110bbfda8aSnia /* 2120bbfda8aSnia * Check if that owner still exists, and if it does, we want 2130bbfda8aSnia * StructureNotify-kind events from it. 2140bbfda8aSnia */ 2150bbfda8aSnia caughtError = false; 2160bbfda8aSnia oldHandler = XSetErrorHandler(CatchError); 2170bbfda8aSnia 2180bbfda8aSnia XSelectInput(dpy, selectionOwner, StructureNotifyMask); 2190bbfda8aSnia XSync(dpy, False); 2200bbfda8aSnia 2210bbfda8aSnia XSetErrorHandler(oldHandler); 2220bbfda8aSnia 2230bbfda8aSnia if(caughtError) { 2240bbfda8aSnia selectionOwner = None; 2250bbfda8aSnia } 2260bbfda8aSnia } 2270bbfda8aSnia 2280bbfda8aSnia /* 2290bbfda8aSnia * If something else is owning things and we're not running 2300bbfda8aSnia * --replace, give up now and return failure. 2310bbfda8aSnia */ 2320bbfda8aSnia if(selectionOwner != None && !CLarg.ewmh_replace) { 2330bbfda8aSnia#ifdef DEBUG_EWMH 2340bbfda8aSnia fprintf(stderr, "A window manager is already running on screen %d\n", 2350bbfda8aSnia scr->screen); 2360bbfda8aSnia#endif 2370bbfda8aSnia return false; 2380bbfda8aSnia } 2390bbfda8aSnia 2400bbfda8aSnia /* We're asked to --replace, so try to do so */ 2410bbfda8aSnia XSetSelectionOwner(dpy, wmAtom, scr->icccm_Window, CurrentTime); 2420bbfda8aSnia 2430bbfda8aSnia if(XGetSelectionOwner(dpy, wmAtom) != scr->icccm_Window) { 2440bbfda8aSnia fprintf(stderr, "Did not get window manager selection on screen %d\n", 2450bbfda8aSnia scr->screen); 2460bbfda8aSnia return false; 2470bbfda8aSnia } 2480bbfda8aSnia 2490bbfda8aSnia /* 2500bbfda8aSnia * If there was a previous selection owner, wait for it 2510bbfda8aSnia * to go away. 2520bbfda8aSnia */ 2530bbfda8aSnia 2540bbfda8aSnia if(selectionOwner != None) { 2550bbfda8aSnia int timeout = 10 * 1000; /* 10 seconds in ms */ 2560bbfda8aSnia XEvent event; 2570bbfda8aSnia struct timespec tosleep; 2580bbfda8aSnia 2590bbfda8aSnia /* Sleep in 100ms chunks */ 2600bbfda8aSnia tosleep.tv_sec = 0; 2610bbfda8aSnia tosleep.tv_nsec = (100 * 1000 * 1000); 2620bbfda8aSnia 2630bbfda8aSnia while(timeout > 0) { 2640bbfda8aSnia 2650bbfda8aSnia int found = XCheckTypedWindowEvent(dpy, selectionOwner, DestroyNotify, &event); 2660bbfda8aSnia if(found) { 2670bbfda8aSnia break; 2680bbfda8aSnia } 2690bbfda8aSnia nanosleep(&tosleep, NULL); 2700bbfda8aSnia timeout -= 100; 2710bbfda8aSnia } 2720bbfda8aSnia 2730bbfda8aSnia if(timeout <= 0) { 2740bbfda8aSnia fprintf(stderr, "Timed out waiting for other window manager " 2750bbfda8aSnia "on screen %d to quit\n", 2760bbfda8aSnia scr->screen); 2770bbfda8aSnia return false; 2780bbfda8aSnia } 2790bbfda8aSnia } 2800bbfda8aSnia 2810bbfda8aSnia /* 2820bbfda8aSnia * Send a message to confirm we're now managing the screen. 2830bbfda8aSnia * ICCCM, end of chapter 2, section "Manager Selections". 2840bbfda8aSnia * http://tronche.com/gui/x/icccm/sec-2.html#s-2.8 2850bbfda8aSnia * 2860bbfda8aSnia * ICCCM says StructureNotifyMask, 2870bbfda8aSnia * OpenBox says SubstructureNotifyMask. 2880bbfda8aSnia */ 2890bbfda8aSnia 2900bbfda8aSnia GenerateTimestamp(scr); 2910bbfda8aSnia 2920bbfda8aSnia SendPropertyMessage(scr->XineramaRoot, scr->XineramaRoot, 2930bbfda8aSnia XA_MANAGER, EventTime, wmAtom, scr->icccm_Window, 0, 0, 2940bbfda8aSnia StructureNotifyMask); 2950bbfda8aSnia 2960bbfda8aSnia return true; 2970bbfda8aSnia} 2980bbfda8aSnia 2990bbfda8aSnia/* 3000bbfda8aSnia * This function is called very early in initialisation. 3010bbfda8aSnia * 3020bbfda8aSnia * Only scr->screen and scr->XineramaRoot are valid: we want to know if 3030bbfda8aSnia * it makes sense to continue with the full initialisation. 3040bbfda8aSnia * 3050bbfda8aSnia * Create the ICCCM window that owns the WM_Sn selection. 3060bbfda8aSnia */ 3070bbfda8aSniabool EwmhInitScreenEarly(ScreenInfo *scr) 3080bbfda8aSnia{ 3090bbfda8aSnia XSetWindowAttributes attrib; 3100bbfda8aSnia 3110bbfda8aSnia#ifdef DEBUG_EWMH 3120bbfda8aSnia fprintf(stderr, "EwmhInitScreenEarly: XCreateWindow\n"); 3130bbfda8aSnia#endif 3140bbfda8aSnia attrib.event_mask = PropertyChangeMask; 3150bbfda8aSnia attrib.override_redirect = True; 3160bbfda8aSnia scr->icccm_Window = XCreateWindow(dpy, scr->XineramaRoot, 3170bbfda8aSnia -100, -100, 1, 1, 0, 3180bbfda8aSnia CopyFromParent, InputOutput, 3190bbfda8aSnia CopyFromParent, 3200bbfda8aSnia CWEventMask | CWOverrideRedirect, 3210bbfda8aSnia &attrib); 3220bbfda8aSnia 3230bbfda8aSnia XMapWindow(dpy, scr->icccm_Window); 3240bbfda8aSnia XLowerWindow(dpy, scr->icccm_Window); 3250bbfda8aSnia 3260bbfda8aSnia#ifdef DEBUG_EWMH 3270bbfda8aSnia fprintf(stderr, "EwmhInitScreenEarly: call EwmhReplaceWM\n"); 3280bbfda8aSnia#endif 3290bbfda8aSnia if(!EwmhReplaceWM(scr)) { 3300bbfda8aSnia XDestroyWindow(dpy, scr->icccm_Window); 3310bbfda8aSnia scr->icccm_Window = None; 3320bbfda8aSnia 3330bbfda8aSnia#ifdef DEBUG_EWMH 3340bbfda8aSnia fprintf(stderr, "EwmhInitScreenEarly: return false\n"); 3350bbfda8aSnia#endif 3360bbfda8aSnia return false; 3370bbfda8aSnia } 3380bbfda8aSnia 3390bbfda8aSnia#ifdef DEBUG_EWMH 3400bbfda8aSnia fprintf(stderr, "EwmhInitScreenEarly: return true\n"); 3410bbfda8aSnia#endif 3420bbfda8aSnia return true; 3430bbfda8aSnia} 3440bbfda8aSnia 3450bbfda8aSnia/* 3460bbfda8aSnia * This initialisation is called late, when scr has been set up 3470bbfda8aSnia * completely. 3480bbfda8aSnia */ 3490bbfda8aSniavoid EwmhInitScreenLate(ScreenInfo *scr) 3500bbfda8aSnia{ 3510bbfda8aSnia long data[2]; 3520bbfda8aSnia 3530bbfda8aSnia /* Set _NET_SUPPORTING_WM_CHECK on root window */ 3540bbfda8aSnia data[0] = scr->icccm_Window; 3550bbfda8aSnia XChangeProperty(dpy, scr->XineramaRoot, 3560bbfda8aSnia XA__NET_SUPPORTING_WM_CHECK, XA_WINDOW, 3570bbfda8aSnia 32, PropModeReplace, 3580bbfda8aSnia (unsigned char *)data, 1); 3590bbfda8aSnia 3600bbfda8aSnia /* 3610bbfda8aSnia * Set properties on the window; 3620bbfda8aSnia * this also belongs with _NET_SUPPORTING_WM_CHECK 3630bbfda8aSnia */ 3640bbfda8aSnia XChangeProperty(dpy, scr->icccm_Window, 3650bbfda8aSnia XA__NET_WM_NAME, XA_UTF8_STRING, 3660bbfda8aSnia 8, PropModeReplace, 3670bbfda8aSnia (unsigned char *)"ctwm", 4); 3680bbfda8aSnia 3690bbfda8aSnia data[0] = scr->icccm_Window; 3700bbfda8aSnia XChangeProperty(dpy, scr->icccm_Window, 3710bbfda8aSnia XA__NET_SUPPORTING_WM_CHECK, XA_WINDOW, 3720bbfda8aSnia 32, PropModeReplace, 3730bbfda8aSnia (unsigned char *)data, 1); 3740bbfda8aSnia 3750bbfda8aSnia /* 3760bbfda8aSnia * Add supported properties to the root window. 3770bbfda8aSnia */ 3780bbfda8aSnia data[0] = 0; 3790bbfda8aSnia data[1] = 0; 3800bbfda8aSnia XChangeProperty(dpy, scr->XineramaRoot, 3810bbfda8aSnia XA__NET_DESKTOP_VIEWPORT, XA_CARDINAL, 3820bbfda8aSnia 32, PropModeReplace, 3830bbfda8aSnia (unsigned char *)data, 2); 3840bbfda8aSnia 3850bbfda8aSnia data[0] = scr->rootw; 3860bbfda8aSnia data[1] = scr->rooth; 3870bbfda8aSnia XChangeProperty(dpy, scr->XineramaRoot, 3880bbfda8aSnia XA__NET_DESKTOP_GEOMETRY, XA_CARDINAL, 3890bbfda8aSnia 32, PropModeReplace, 3900bbfda8aSnia (unsigned char *)data, 2); 3910bbfda8aSnia 3920bbfda8aSnia EwmhSet_NET_WORKAREA(scr); 3930bbfda8aSnia 3940bbfda8aSnia if(scr->workSpaceManagerActive) { 3950bbfda8aSnia data[0] = scr->workSpaceMgr.count; 3960bbfda8aSnia } 3970bbfda8aSnia else { 3980bbfda8aSnia data[0] = 1; 3990bbfda8aSnia } 4000bbfda8aSnia 4010bbfda8aSnia XChangeProperty(dpy, scr->XineramaRoot, 4020bbfda8aSnia XA__NET_NUMBER_OF_DESKTOPS, XA_CARDINAL, 4030bbfda8aSnia 32, PropModeReplace, 4040bbfda8aSnia (unsigned char *)data, 1); 4050bbfda8aSnia 4060bbfda8aSnia if(scr->workSpaceManagerActive) { 4070bbfda8aSnia /* TODO: this is for the first Virtual Screen only... */ 4080bbfda8aSnia /*data[0] = scr->workSpaceMgr.workSpaceWindowList->currentwspc->number; */ 4090bbfda8aSnia data[0] = 0; 4100bbfda8aSnia } 4110bbfda8aSnia else { 4120bbfda8aSnia data[0] = 0; 4130bbfda8aSnia } 4140bbfda8aSnia XChangeProperty(dpy, scr->XineramaRoot, 4150bbfda8aSnia XA__NET_CURRENT_DESKTOP, XA_CARDINAL, 4160bbfda8aSnia 32, PropModeReplace, 4170bbfda8aSnia (unsigned char *)data, 1); 4180bbfda8aSnia 4190bbfda8aSnia EwmhSet_NET_SHOWING_DESKTOP(0); 4200bbfda8aSnia 4210bbfda8aSnia long supported[30]; 4220bbfda8aSnia int i = 0; 4230bbfda8aSnia 4240bbfda8aSnia supported[i++] = XA__NET_SUPPORTING_WM_CHECK; 4250bbfda8aSnia supported[i++] = XA__NET_DESKTOP_VIEWPORT; 4260bbfda8aSnia supported[i++] = XA__NET_NUMBER_OF_DESKTOPS; 4270bbfda8aSnia supported[i++] = XA__NET_CURRENT_DESKTOP; 4280bbfda8aSnia supported[i++] = XA__NET_DESKTOP_GEOMETRY; 4290bbfda8aSnia supported[i++] = XA__NET_WM_ICON; 4300bbfda8aSnia supported[i++] = XA__NET_WM_DESKTOP; 4310bbfda8aSnia supported[i++] = XA__NET_CLIENT_LIST; 4320bbfda8aSnia supported[i++] = XA__NET_CLIENT_LIST_STACKING; 4330bbfda8aSnia supported[i++] = XA__NET_WM_WINDOW_TYPE; 4340bbfda8aSnia supported[i++] = XA__NET_WM_WINDOW_TYPE_NORMAL; 4350bbfda8aSnia supported[i++] = XA__NET_WM_WINDOW_TYPE_DESKTOP; 4360bbfda8aSnia supported[i++] = XA__NET_WM_WINDOW_TYPE_DOCK; 4370bbfda8aSnia supported[i++] = XA__NET_WM_STRUT; 4380bbfda8aSnia supported[i++] = XA__NET_WM_STRUT_PARTIAL; 4390bbfda8aSnia supported[i++] = XA__NET_SHOWING_DESKTOP; 4400bbfda8aSnia supported[i++] = XA__NET_WM_STATE; 4410bbfda8aSnia supported[i++] = XA__NET_WM_STATE_MAXIMIZED_VERT; 4420bbfda8aSnia supported[i++] = XA__NET_WM_STATE_MAXIMIZED_HORZ; 4430bbfda8aSnia supported[i++] = XA__NET_WM_STATE_FULLSCREEN; 4440bbfda8aSnia supported[i++] = XA__NET_ACTIVE_WINDOW; 4450bbfda8aSnia supported[i++] = XA__NET_WORKAREA; 4460bbfda8aSnia supported[i++] = XA__NET_WM_MOVERESIZE; 4470bbfda8aSnia supported[i++] = XA__NET_WM_STATE_SHADED; 4480bbfda8aSnia supported[i++] = XA__NET_WM_STATE_ABOVE; 4490bbfda8aSnia supported[i++] = XA__NET_WM_STATE_BELOW; 450b18c2d1eSnia supported[i++] = XA__NET_CLOSE_WINDOW; 4510bbfda8aSnia 4520bbfda8aSnia XChangeProperty(dpy, scr->XineramaRoot, 4530bbfda8aSnia XA__NET_SUPPORTED, XA_ATOM, 4540bbfda8aSnia 32, PropModeReplace, 4550bbfda8aSnia (unsigned char *)supported, i); 4560bbfda8aSnia} 4570bbfda8aSnia 458b18c2d1eSnia 459b18c2d1eSnia#ifdef VSCREEN 4600bbfda8aSnia/* 4610bbfda8aSnia * Set up the _NET_VIRTUAL_ROOTS property, which indicates that we're 4620bbfda8aSnia * using virtual root windows. 4630bbfda8aSnia * This applies only if we have multiple virtual screens. 4640bbfda8aSnia * 4650bbfda8aSnia * Also record this as a supported atom in _NET_SUPPORTED. 4660bbfda8aSnia * 4670bbfda8aSnia * Really, our virtual screens (with their virtual root windows) don't quite 4680bbfda8aSnia * fit in the EWMH idiom. Several root window properties (such as 4690bbfda8aSnia * _NET_CURRENT_DESKTOP) are more appropriate on the virtual root windows. But 4700bbfda8aSnia * that is not where other clients would look for them. 4710bbfda8aSnia * 4720bbfda8aSnia * The idea seems to be that the virtual roots as used for workspaces (desktops 4730bbfda8aSnia * in EWMH terminology) are only mapped one at a time. 4740bbfda8aSnia */ 4750bbfda8aSniavoid EwmhInitVirtualRoots(ScreenInfo *scr) 4760bbfda8aSnia{ 4770bbfda8aSnia int numVscreens = scr->numVscreens; 4780bbfda8aSnia 4790bbfda8aSnia if(numVscreens > 1) { 4800bbfda8aSnia long *data; 4810bbfda8aSnia long d0; 4820bbfda8aSnia VirtualScreen *vs; 4830bbfda8aSnia int i; 4840bbfda8aSnia 4850bbfda8aSnia data = calloc(numVscreens, sizeof(long)); 4860bbfda8aSnia 4870bbfda8aSnia for(vs = scr->vScreenList, i = 0; 4880bbfda8aSnia vs != NULL && i < numVscreens; 4890bbfda8aSnia vs = vs->next, i++) { 4900bbfda8aSnia data[i] = vs->window; 4910bbfda8aSnia } 4920bbfda8aSnia 4930bbfda8aSnia XChangeProperty(dpy, scr->XineramaRoot, 4940bbfda8aSnia XA__NET_VIRTUAL_ROOTS, XA_WINDOW, 4950bbfda8aSnia 32, PropModeReplace, 4960bbfda8aSnia (unsigned char *)data, numVscreens); 4970bbfda8aSnia 4980bbfda8aSnia /* This might fail, but what can we do about it? */ 4990bbfda8aSnia 5000bbfda8aSnia free(data); 5010bbfda8aSnia 5020bbfda8aSnia d0 = XA__NET_VIRTUAL_ROOTS; 5030bbfda8aSnia XChangeProperty(dpy, scr->XineramaRoot, 5040bbfda8aSnia XA__NET_SUPPORTED, XA_ATOM, 5050bbfda8aSnia 32, PropModeAppend, 5060bbfda8aSnia (unsigned char *)&d0, 1); 5070bbfda8aSnia } 5080bbfda8aSnia} 509b18c2d1eSnia#endif 510b18c2d1eSnia 5110bbfda8aSnia 5120bbfda8aSniastatic void EwmhTerminateScreen(ScreenInfo *scr) 5130bbfda8aSnia{ 5140bbfda8aSnia XDeleteProperty(dpy, scr->XineramaRoot, XA__NET_SUPPORTED); 5150bbfda8aSnia 5160bbfda8aSnia /* 5170bbfda8aSnia * Don't delete scr->icccm_Window; let it be deleted automatically 5180bbfda8aSnia * when we terminate the X server connection. A replacement window 5190bbfda8aSnia * manager may want to start working immediately after it has 5200bbfda8aSnia * disappeared. 5210bbfda8aSnia */ 5220bbfda8aSnia} 5230bbfda8aSnia 5240bbfda8aSnia/* 5250bbfda8aSnia * Clear everything that needs to be cleared before we exit. 5260bbfda8aSnia */ 5270bbfda8aSnia 5280bbfda8aSniavoid EwmhTerminate(void) 5290bbfda8aSnia{ 5300bbfda8aSnia int scrnum; 5310bbfda8aSnia ScreenInfo *scr; 5320bbfda8aSnia 5330bbfda8aSnia for(scrnum = 0; scrnum < NumScreens; scrnum++) { 5340bbfda8aSnia if((scr = ScreenList[scrnum]) == NULL) { 5350bbfda8aSnia continue; 5360bbfda8aSnia } 5370bbfda8aSnia EwmhTerminateScreen(scr); 5380bbfda8aSnia } 5390bbfda8aSnia} 5400bbfda8aSnia 5410bbfda8aSnia/* 5420bbfda8aSnia * Event handler: lost the WM_Sn selection 5430bbfda8aSnia * (that's the only selection we have). 5440bbfda8aSnia */ 5450bbfda8aSnia 5460bbfda8aSniavoid EwmhSelectionClear(XSelectionClearEvent *sev) 5470bbfda8aSnia{ 5480bbfda8aSnia#ifdef DEBUG_EWMH 5490bbfda8aSnia fprintf(stderr, "sev->window = %x\n", (unsigned)sev->window); 5500bbfda8aSnia#endif 551b18c2d1eSnia DoShutdown(); 5520bbfda8aSnia} 5530bbfda8aSnia 5540bbfda8aSnia/* 5550bbfda8aSnia * When accepting client messages to the root window, 5560bbfda8aSnia * be accepting and accept both the real root window and the 5570bbfda8aSnia * current virtual screen. 5580bbfda8aSnia * 5590bbfda8aSnia * Should perhaps also accept any other virtual screen. 5600bbfda8aSnia */ 5610bbfda8aSniabool EwmhClientMessage(XClientMessageEvent *msg) 5620bbfda8aSnia{ 5630bbfda8aSnia if(msg->format != 32) { 5640bbfda8aSnia return false; 5650bbfda8aSnia } 5660bbfda8aSnia 5670bbfda8aSnia /* Messages regarding any window */ 5680bbfda8aSnia if(msg->message_type == XA__NET_WM_DESKTOP) { 5690bbfda8aSnia EwmhClientMessage_NET_WM_DESKTOP(msg); 5700bbfda8aSnia return true; 5710bbfda8aSnia } 5720bbfda8aSnia else if(msg->message_type == XA__NET_WM_STATE) { 5730bbfda8aSnia EwmhClientMessage_NET_WM_STATE(msg); 5740bbfda8aSnia return true; 5750bbfda8aSnia } 5760bbfda8aSnia else if(msg->message_type == XA__NET_ACTIVE_WINDOW) { 5770bbfda8aSnia EwmhClientMessage_NET_ACTIVE_WINDOW(msg); 5780bbfda8aSnia return true; 5790bbfda8aSnia } 5800bbfda8aSnia else if(msg->message_type == XA__NET_WM_MOVERESIZE) { 5810bbfda8aSnia EwmhClientMessage_NET_WM_MOVERESIZE(msg); 5820bbfda8aSnia return true; 5830bbfda8aSnia } 584b18c2d1eSnia else if(msg->message_type == XA__NET_CLOSE_WINDOW) { 585b18c2d1eSnia EwmhClientMessage_NET_CLOSE_WINDOW(msg); 586b18c2d1eSnia return true; 587b18c2d1eSnia } 5880bbfda8aSnia 5890bbfda8aSnia /* Messages regarding the root window */ 5900bbfda8aSnia if(msg->window != Scr->XineramaRoot && 5910bbfda8aSnia msg->window != Scr->Root) { 5920bbfda8aSnia#ifdef DEBUG_EWMH 5930bbfda8aSnia fprintf(stderr, "Received unrecognized client message: %s\n", 5940bbfda8aSnia XGetAtomName(dpy, msg->message_type)); 5950bbfda8aSnia#endif 5960bbfda8aSnia return false; 5970bbfda8aSnia } 5980bbfda8aSnia 5990bbfda8aSnia if(msg->message_type == XA__NET_CURRENT_DESKTOP) { 6000bbfda8aSnia GotoWorkSpaceByNumber(Scr->currentvs, msg->data.l[0]); 6010bbfda8aSnia return true; 6020bbfda8aSnia } 6030bbfda8aSnia else if(msg->message_type == XA__NET_SHOWING_DESKTOP) { 6040bbfda8aSnia ShowBackground(Scr->currentvs, msg->data.l[0] ? 1 : 0); 605b18c2d1eSnia return true; 6060bbfda8aSnia } 6070bbfda8aSnia else { 6080bbfda8aSnia#ifdef DEBUG_EWMH 6090bbfda8aSnia fprintf(stderr, "Received unrecognized client message about root window: %s\n", 6100bbfda8aSnia XGetAtomName(dpy, msg->message_type)); 6110bbfda8aSnia#endif 6120bbfda8aSnia } 6130bbfda8aSnia 6140bbfda8aSnia return false; 6150bbfda8aSnia} 6160bbfda8aSnia 6170bbfda8aSnia/* 6180bbfda8aSnia * The format of the _NET_WM_ICON property is 6190bbfda8aSnia * 6200bbfda8aSnia * [0] width 6210bbfda8aSnia * [1] height 6220bbfda8aSnia * height repetitions of 6230bbfda8aSnia * row, which is 6240bbfda8aSnia * width repetitions of 6250bbfda8aSnia * pixel: ARGB 6260bbfda8aSnia * repeat for next size. 6270bbfda8aSnia * 6280bbfda8aSnia * Some icons can be 256x256 CARDINALs which is 65536 CARDINALS! 6290bbfda8aSnia * Therefore we fetch in pieces and skip the pixels of large icons 6300bbfda8aSnia * until needed. 6310bbfda8aSnia * 6320bbfda8aSnia * First scan all sizes. Keep a record of the closest smaller and larger 6330bbfda8aSnia * size. At the end, choose from one of those. 6340bbfda8aSnia * Finally, go and fetch the pixel data. 6350bbfda8aSnia */ 6360bbfda8aSnia 6370bbfda8aSniaImage *EwmhGetIcon(ScreenInfo *scr, TwmWindow *twm_win) 6380bbfda8aSnia{ 6390bbfda8aSnia int fetch_offset; 6400bbfda8aSnia Atom actual_type; 6410bbfda8aSnia int actual_format; 6420bbfda8aSnia unsigned long nitems, bytes_after; 6430bbfda8aSnia unsigned long *prop; 6440bbfda8aSnia 6450bbfda8aSnia int wanted_area; 6460bbfda8aSnia int smaller, larger; 6470bbfda8aSnia int offset; 6480bbfda8aSnia int smaller_offset, larger_offset; 6490bbfda8aSnia int i; 6500bbfda8aSnia 651b18c2d1eSnia int width, height; 6520bbfda8aSnia 6530bbfda8aSnia fetch_offset = 0; 6540bbfda8aSnia if(XGetWindowProperty(dpy, twm_win->w, XA__NET_WM_ICON, 6550bbfda8aSnia fetch_offset, 8 * 1024, False, XA_CARDINAL, 6560bbfda8aSnia &actual_type, &actual_format, &nitems, 6570bbfda8aSnia &bytes_after, (unsigned char **)&prop) != Success || nitems == 0) { 6580bbfda8aSnia return NULL; 6590bbfda8aSnia } 6600bbfda8aSnia 6610bbfda8aSnia if(actual_format != 32) { 6620bbfda8aSnia XFree(prop); 6630bbfda8aSnia return NULL; 6640bbfda8aSnia } 6650bbfda8aSnia 6660bbfda8aSnia#ifdef DEBUG_EWMH 6670bbfda8aSnia fprintf(stderr, "_NET_WM_ICON data fetched\n"); 6680bbfda8aSnia#endif 6690bbfda8aSnia /* 6700bbfda8aSnia * Usually the icons are square, but that is not a rule. 6710bbfda8aSnia * So we measure the area instead. 6720bbfda8aSnia * 6730bbfda8aSnia * Approach wanted size from both directions and at the end, 6740bbfda8aSnia * choose the "nearest". 6750bbfda8aSnia */ 6760bbfda8aSnia wanted_area = Scr->PreferredIconWidth * Scr->PreferredIconHeight; 6770bbfda8aSnia smaller = 0; 6780bbfda8aSnia larger = 999999; 6790bbfda8aSnia smaller_offset = -1; 6800bbfda8aSnia larger_offset = -1; 6810bbfda8aSnia i = 0; 6820bbfda8aSnia 6830bbfda8aSnia for(;;) { 6840bbfda8aSnia offset = i; 6850bbfda8aSnia 6860bbfda8aSnia int w = prop[i++]; 6870bbfda8aSnia int h = prop[i++]; 6880bbfda8aSnia int size = w * h; 6890bbfda8aSnia 690b18c2d1eSnia const int area = w * h; 6910bbfda8aSnia 6920bbfda8aSnia#ifdef DEBUG_EWMH 6930bbfda8aSnia fprintf(stderr, "[%d+%d] w=%d h=%d\n", fetch_offset, offset, w, h); 6940bbfda8aSnia#endif 6950bbfda8aSnia 6960bbfda8aSnia 6970bbfda8aSnia if(area == wanted_area) { 6980bbfda8aSnia#ifdef DEBUG_EWMH 6990bbfda8aSnia fprintf(stderr, "exact match [%d+%d=%d] w=%d h=%d\n", fetch_offset, offset, 7000bbfda8aSnia fetch_offset + offset, w, h); 7010bbfda8aSnia#endif /* DEBUG_EWMH */ 7020bbfda8aSnia smaller_offset = fetch_offset + offset; 7030bbfda8aSnia smaller = area; 7040bbfda8aSnia larger_offset = -1; 7050bbfda8aSnia break; 7060bbfda8aSnia } 7070bbfda8aSnia else if(area < wanted_area) { 7080bbfda8aSnia if(area > smaller) { 7090bbfda8aSnia#ifdef DEBUG_EWMH 7100bbfda8aSnia fprintf(stderr, "increase smaller, was [%d]\n", smaller_offset); 7110bbfda8aSnia#endif /* DEBUG_EWMH */ 7120bbfda8aSnia smaller = area; 7130bbfda8aSnia smaller_offset = fetch_offset + offset; 7140bbfda8aSnia } 7150bbfda8aSnia } 7160bbfda8aSnia else { /* area > wanted_area */ 7170bbfda8aSnia if(area < larger) { 7180bbfda8aSnia#ifdef DEBUG_EWMH 7190bbfda8aSnia fprintf(stderr, "decrease larger, was [%d]\n", larger_offset); 7200bbfda8aSnia#endif /* DEBUG_EWMH */ 7210bbfda8aSnia larger = area; 7220bbfda8aSnia larger_offset = fetch_offset + offset; 7230bbfda8aSnia } 7240bbfda8aSnia } 7250bbfda8aSnia 7260bbfda8aSnia if(i + size + 2 > nitems) { 7270bbfda8aSnia#ifdef DEBUG_EWMH 7280bbfda8aSnia fprintf(stderr, "not enough data: %d + %d > %ld \n", i, size, nitems); 7290bbfda8aSnia#endif /* DEBUG_EWMH */ 7300bbfda8aSnia 7310bbfda8aSnia if(i + size + 2 <= nitems + bytes_after / 4) { 7320bbfda8aSnia /* we can fetch some more... */ 7330bbfda8aSnia XFree(prop); 7340bbfda8aSnia fetch_offset += i + size; 7350bbfda8aSnia if(XGetWindowProperty(dpy, twm_win->w, XA__NET_WM_ICON, 7360bbfda8aSnia fetch_offset, 8 * 1024, False, XA_CARDINAL, 7370bbfda8aSnia &actual_type, &actual_format, &nitems, 7380bbfda8aSnia &bytes_after, (unsigned char **)&prop) != Success) { 7390bbfda8aSnia continue; 7400bbfda8aSnia } 7410bbfda8aSnia i = 0; 7420bbfda8aSnia continue; 7430bbfda8aSnia } 7440bbfda8aSnia break; 7450bbfda8aSnia } 7460bbfda8aSnia i += size; 7470bbfda8aSnia } 7480bbfda8aSnia 7490bbfda8aSnia /* 7500bbfda8aSnia * Choose which icon approximates our desired size best. 7510bbfda8aSnia */ 752b18c2d1eSnia int area = 0; 753b18c2d1eSnia ALLOW_DEAD_STORE(area); // all branches below init it 7540bbfda8aSnia 7550bbfda8aSnia if(smaller_offset >= 0) { 7560bbfda8aSnia if(larger_offset >= 0) { 7570bbfda8aSnia /* choose the nearest */ 7580bbfda8aSnia#ifdef DEBUG_EWMH 7590bbfda8aSnia fprintf(stderr, "choose nearest %d %d\n", smaller, larger); 7600bbfda8aSnia#endif /* DEBUG_EWMH */ 7610bbfda8aSnia if((double)larger / wanted_area > (double)wanted_area / smaller) { 7620bbfda8aSnia offset = smaller_offset; 7630bbfda8aSnia area = smaller; 7640bbfda8aSnia } 7650bbfda8aSnia else { 7660bbfda8aSnia offset = larger_offset; 7670bbfda8aSnia area = larger; 7680bbfda8aSnia } 7690bbfda8aSnia } 7700bbfda8aSnia else { 7710bbfda8aSnia /* choose smaller */ 7720bbfda8aSnia#ifdef DEBUG_EWMH 7730bbfda8aSnia fprintf(stderr, "choose smaller (only) %d\n", smaller); 7740bbfda8aSnia#endif /* DEBUG_EWMH */ 7750bbfda8aSnia offset = smaller_offset; 7760bbfda8aSnia area = smaller; 7770bbfda8aSnia } 7780bbfda8aSnia } 7790bbfda8aSnia else if(larger_offset >= 0) { 7800bbfda8aSnia /* choose larger */ 7810bbfda8aSnia#ifdef DEBUG_EWMH 7820bbfda8aSnia fprintf(stderr, "choose larger (only) %d\n", larger); 7830bbfda8aSnia#endif /* DEBUG_EWMH */ 7840bbfda8aSnia offset = larger_offset; 7850bbfda8aSnia area = larger; 7860bbfda8aSnia } 7870bbfda8aSnia else { 7880bbfda8aSnia /* no icons found at all? */ 7890bbfda8aSnia#ifdef DEBUG_EWMH 7900bbfda8aSnia fprintf(stderr, "nothing to choose from\n"); 7910bbfda8aSnia#endif /* DEBUG_EWMH */ 7920bbfda8aSnia XFree(prop); 7930bbfda8aSnia return NULL; 7940bbfda8aSnia } 7950bbfda8aSnia 7960bbfda8aSnia /* 7970bbfda8aSnia * Now fetch the pixels. 7980bbfda8aSnia */ 7990bbfda8aSnia 8000bbfda8aSnia#ifdef DEBUG_EWMH 8010bbfda8aSnia fprintf(stderr, "offset = %d fetch_offset = %d\n", offset, fetch_offset); 8020bbfda8aSnia fprintf(stderr, "offset + 2 + area = %d fetch_offset + nitems = %ld\n", 8030bbfda8aSnia offset + 2 + area, fetch_offset + nitems); 8040bbfda8aSnia#endif /* DEBUG_EWMH */ 8050bbfda8aSnia if(offset < fetch_offset || 8060bbfda8aSnia offset + 2 + area > fetch_offset + nitems) { 8070bbfda8aSnia XFree(prop); 8080bbfda8aSnia fetch_offset = offset; 8090bbfda8aSnia#ifdef DEBUG_EWMH 8100bbfda8aSnia fprintf(stderr, "refetching from %d\n", fetch_offset); 8110bbfda8aSnia#endif /* DEBUG_EWMH */ 8120bbfda8aSnia if(XGetWindowProperty(dpy, twm_win->w, XA__NET_WM_ICON, 8130bbfda8aSnia fetch_offset, 2 + area, False, XA_CARDINAL, 8140bbfda8aSnia &actual_type, &actual_format, &nitems, 8150bbfda8aSnia &bytes_after, (unsigned char **)&prop) != Success) { 8160bbfda8aSnia return NULL; 8170bbfda8aSnia } 8180bbfda8aSnia } 8190bbfda8aSnia 8200bbfda8aSnia i = offset - fetch_offset; 8210bbfda8aSnia width = prop[i++]; 8220bbfda8aSnia height = prop[i++]; 8230bbfda8aSnia#ifdef DEBUG_EWMH 8240bbfda8aSnia fprintf(stderr, "Chosen [%d] w=%d h=%d area=%d\n", offset, width, height, area); 8250bbfda8aSnia#endif /* DEBUG_EWMH */ 8260bbfda8aSnia assert(width * height == area); 8270bbfda8aSnia 8280bbfda8aSnia Image *image = ExtractIcon(scr, &prop[i], width, height); 8290bbfda8aSnia 8300bbfda8aSnia XFree(prop); 8310bbfda8aSnia 8320bbfda8aSnia return image; 8330bbfda8aSnia} 8340bbfda8aSnia 8350bbfda8aSniastatic uint16_t *buffer_16bpp; 8360bbfda8aSniastatic uint32_t *buffer_32bpp; 8370bbfda8aSnia 8380bbfda8aSniastatic void convert_for_16(int w, int x, int y, int argb) 8390bbfda8aSnia{ 8400bbfda8aSnia int r = (argb >> 16) & 0xFF; 8410bbfda8aSnia int g = (argb >> 8) & 0xFF; 8420bbfda8aSnia int b = (argb >> 0) & 0xFF; 8430bbfda8aSnia buffer_16bpp [y * w + x] = ((r >> 3) << 11) + ((g >> 2) << 5) + (b >> 3); 8440bbfda8aSnia} 8450bbfda8aSnia 8460bbfda8aSniastatic void convert_for_32(int w, int x, int y, int argb) 8470bbfda8aSnia{ 8480bbfda8aSnia buffer_32bpp [y * w + x] = argb & 0x00FFFFFF; 8490bbfda8aSnia} 8500bbfda8aSnia 8510bbfda8aSniastatic Image *ExtractIcon(ScreenInfo *scr, unsigned long *prop, int width, 8520bbfda8aSnia int height) 8530bbfda8aSnia{ 8540bbfda8aSnia XImage *ximage; 8550bbfda8aSnia void (*store_data)(int w, int x, int y, int argb); 8560bbfda8aSnia int x, y, transparency; 8570bbfda8aSnia int rowbytes; 8580bbfda8aSnia unsigned char *maskbits; 8590bbfda8aSnia 8600bbfda8aSnia GC gc; 8610bbfda8aSnia Pixmap pixret; 8620bbfda8aSnia Pixmap mask; 8630bbfda8aSnia Image *image; 8640bbfda8aSnia int i; 8650bbfda8aSnia 8660bbfda8aSnia ximage = NULL; 8670bbfda8aSnia 8680bbfda8aSnia /** XXX sort of duplicated from util.c:LoadJpegImage() */ 8690bbfda8aSnia if(scr->d_depth == 16) { 8700bbfda8aSnia store_data = convert_for_16; 8710bbfda8aSnia buffer_16bpp = malloc(width * height * sizeof(buffer_16bpp[0])); 8720bbfda8aSnia buffer_32bpp = NULL; 8730bbfda8aSnia ximage = XCreateImage(dpy, CopyFromParent, scr->d_depth, ZPixmap, 0, 8740bbfda8aSnia (char *) buffer_16bpp, width, height, 16, width * 2); 8750bbfda8aSnia } 8760bbfda8aSnia else if(scr->d_depth == 24 || scr->d_depth == 32) { 8770bbfda8aSnia store_data = convert_for_32; 8780bbfda8aSnia buffer_32bpp = malloc(width * height * sizeof(buffer_32bpp[0])); 8790bbfda8aSnia buffer_16bpp = NULL; 8800bbfda8aSnia ximage = XCreateImage(dpy, CopyFromParent, scr->d_depth, ZPixmap, 0, 8810bbfda8aSnia (char *) buffer_32bpp, width, height, 32, width * 4); 8820bbfda8aSnia } 8830bbfda8aSnia else { 8840bbfda8aSnia#ifdef DEBUG_EWMH 8850bbfda8aSnia fprintf(stderr, "Screen unsupported depth for 32-bit icon: %d\n", scr->d_depth); 8860bbfda8aSnia#endif /* DEBUG_EWMH */ 8870bbfda8aSnia XFree(prop); 8880bbfda8aSnia return NULL; 8890bbfda8aSnia } 8900bbfda8aSnia if(ximage == NULL) { 8910bbfda8aSnia#ifdef DEBUG_EWMH 8920bbfda8aSnia fprintf(stderr, "cannot create image for icon\n"); 8930bbfda8aSnia#endif /* DEBUG_EWMH */ 8940bbfda8aSnia XFree(prop); 8950bbfda8aSnia return NULL; 8960bbfda8aSnia } 8970bbfda8aSnia 8980bbfda8aSnia transparency = 0; 8990bbfda8aSnia rowbytes = (width + 7) / 8; 9000bbfda8aSnia maskbits = calloc(height, rowbytes); 9010bbfda8aSnia 9020bbfda8aSnia /* 9030bbfda8aSnia * Copy all ARGB pixels to the pixmap (the RGB part), and the bitmap (the 9040bbfda8aSnia * Alpha, or opaqueness part). If any pixels are transparent, we're going 9050bbfda8aSnia * to need a shape. 9060bbfda8aSnia */ 9070bbfda8aSnia i = 0; 9080bbfda8aSnia for(y = 0; y < height; y++) { 9090bbfda8aSnia for(x = 0; x < width; x++) { 9100bbfda8aSnia unsigned long argb = prop[i++]; 9110bbfda8aSnia store_data(width, x, y, argb); 9120bbfda8aSnia int opaque = ((argb >> 24) & 0xFF) >= 0x80; /* arbitrary cutoff */ 9130bbfda8aSnia if(opaque) { 9140bbfda8aSnia maskbits [rowbytes * y + (x / 8)] |= 0x01 << (x % 8); 9150bbfda8aSnia } 9160bbfda8aSnia else { 9170bbfda8aSnia transparency = 1; 9180bbfda8aSnia } 9190bbfda8aSnia } 9200bbfda8aSnia } 9210bbfda8aSnia 9220bbfda8aSnia gc = DefaultGC(dpy, scr->screen); 9230bbfda8aSnia pixret = XCreatePixmap(dpy, scr->Root, width, height, scr->d_depth); 9240bbfda8aSnia XPutImage(dpy, pixret, gc, ximage, 0, 0, 0, 0, width, height); 9250bbfda8aSnia XDestroyImage(ximage); /* also frees buffer_{16,32}bpp */ 9260bbfda8aSnia ximage = NULL; 9270bbfda8aSnia 9280bbfda8aSnia mask = None; 9290bbfda8aSnia if(transparency) { 9300bbfda8aSnia mask = XCreatePixmapFromBitmapData(dpy, scr->Root, (char *)maskbits, 9310bbfda8aSnia width, height, 1, 0, 1); 9320bbfda8aSnia } 9330bbfda8aSnia free(maskbits); 9340bbfda8aSnia 9350bbfda8aSnia image = AllocImage(); 9360bbfda8aSnia image->width = width; 9370bbfda8aSnia image->height = height; 9380bbfda8aSnia image->pixmap = pixret; 9390bbfda8aSnia image->mask = mask; 9400bbfda8aSnia 9410bbfda8aSnia return image; 9420bbfda8aSnia} 9430bbfda8aSnia 9440bbfda8aSnia/* 9450bbfda8aSnia * Handle a PropertyNotify on _NET_WM_ICON. 9460bbfda8aSnia */ 9470bbfda8aSniastatic void EwmhHandle_NET_WM_ICONNotify(XPropertyEvent *event, 9480bbfda8aSnia TwmWindow *twm_win) 9490bbfda8aSnia{ 9500bbfda8aSnia unsigned long valuemask; /* mask for create windows */ 9510bbfda8aSnia XSetWindowAttributes attributes; /* attributes for create windows */ 9520bbfda8aSnia Icon *icon = twm_win->icon; 9530bbfda8aSnia int x; 9540bbfda8aSnia 9550bbfda8aSnia#ifdef DEBUG_EWMH 9560bbfda8aSnia fprintf(stderr, "EwmhHandlePropertyNotify: NET_WM_ICON\n"); 9570bbfda8aSnia#endif /* DEBUG_EWMH */ 9580bbfda8aSnia /* 9590bbfda8aSnia * If there is no icon yet, we'll look at this property 9600bbfda8aSnia * later, if and when we do create an icon. 9610bbfda8aSnia */ 9620bbfda8aSnia if(!icon || icon->match != match_net_wm_icon) { 9630bbfda8aSnia#ifdef DEBUG_EWMH 9640bbfda8aSnia fprintf(stderr, "no icon, or not match_net_wm_icon\n"); 9650bbfda8aSnia#endif /* DEBUG_EWMH */ 9660bbfda8aSnia return; 9670bbfda8aSnia } 9680bbfda8aSnia 9690bbfda8aSnia Image *image = EwmhGetIcon(Scr, twm_win); 9700bbfda8aSnia 9710bbfda8aSnia /* TODO: de-duplicate with handling of XA_WM_HINTS */ 9720bbfda8aSnia { 9730bbfda8aSnia Image *old_image = icon->image; 9740bbfda8aSnia icon->image = image; 9750bbfda8aSnia FreeImage(old_image); 9760bbfda8aSnia } 9770bbfda8aSnia 9780bbfda8aSnia 9790bbfda8aSnia if(twm_win->icon->bm_w) { 9800bbfda8aSnia XDestroyWindow(dpy, twm_win->icon->bm_w); 9810bbfda8aSnia } 9820bbfda8aSnia 9830bbfda8aSnia valuemask = CWBackPixmap; 9840bbfda8aSnia attributes.background_pixmap = image->pixmap; 9850bbfda8aSnia 9860bbfda8aSnia x = GetIconOffset(twm_win->icon); 9870bbfda8aSnia twm_win->icon->bm_w = 9880bbfda8aSnia XCreateWindow(dpy, twm_win->icon->w, x, 0, 9890bbfda8aSnia twm_win->icon->width, 9900bbfda8aSnia twm_win->icon->height, 9910bbfda8aSnia 0, Scr->d_depth, 9920bbfda8aSnia CopyFromParent, Scr->d_visual, 9930bbfda8aSnia valuemask, &attributes); 9940bbfda8aSnia 9950bbfda8aSnia if(image->mask) { 9960bbfda8aSnia XShapeCombineMask(dpy, twm_win->icon->bm_w, ShapeBounding, 0, 0, image->mask, 9970bbfda8aSnia ShapeSet); 9980bbfda8aSnia XShapeCombineMask(dpy, twm_win->icon->w, ShapeBounding, x, 0, image->mask, 9990bbfda8aSnia ShapeSet); 10000bbfda8aSnia } 10010bbfda8aSnia else { 10020bbfda8aSnia XRectangle rect; 10030bbfda8aSnia 10040bbfda8aSnia rect.x = x; 10050bbfda8aSnia rect.y = 0; 10060bbfda8aSnia rect.width = twm_win->icon->width; 10070bbfda8aSnia rect.height = twm_win->icon->height; 10080bbfda8aSnia XShapeCombineRectangles(dpy, twm_win->icon->w, ShapeBounding, 0, 10090bbfda8aSnia 0, &rect, 1, ShapeUnion, 0); 10100bbfda8aSnia } 10110bbfda8aSnia XMapSubwindows(dpy, twm_win->icon->w); 10120bbfda8aSnia RedoIconName(twm_win); 10130bbfda8aSnia} 10140bbfda8aSnia 10150bbfda8aSnia/* 10160bbfda8aSnia * Handle a PropertyNotify on _NET_WM_STRUT(PARTIAL). 10170bbfda8aSnia */ 10180bbfda8aSniastatic void EwmhHandle_NET_WM_STRUTNotify(XPropertyEvent *event, 10190bbfda8aSnia TwmWindow *twm_win) 10200bbfda8aSnia{ 10210bbfda8aSnia EwmhGetStrut(twm_win, true); 10220bbfda8aSnia} 10230bbfda8aSnia 10240bbfda8aSnia/* 10250bbfda8aSnia * Handle a _NET_WM_STATE ClientMessage. 10260bbfda8aSnia */ 10270bbfda8aSniastatic int atomToFlag(Atom a) 10280bbfda8aSnia{ 10290bbfda8aSnia#ifdef DEBUG_EWMH 10300bbfda8aSnia# define CRWARN(x) fprintf(stderr, "atomToFlag: ignoring " #x "\n") 10310bbfda8aSnia#else 10320bbfda8aSnia# define CRWARN(x) (void)0 10330bbfda8aSnia#endif 10340bbfda8aSnia#define CHKNRET(st) \ 10350bbfda8aSnia if(a == XA__NET_WM_##st) { \ 10360bbfda8aSnia if(LookInNameList(Scr->EWMHIgnore, #st)) { \ 10370bbfda8aSnia CRWARN(st); \ 10380bbfda8aSnia return 0; \ 10390bbfda8aSnia } \ 10400bbfda8aSnia return EWMH_##st; \ 10410bbfda8aSnia } 10420bbfda8aSnia 10430bbfda8aSnia /* Check (potentially ignoring) various flags we know */ 10440bbfda8aSnia CHKNRET(STATE_MAXIMIZED_VERT); 10450bbfda8aSnia CHKNRET(STATE_MAXIMIZED_HORZ); 10460bbfda8aSnia CHKNRET(STATE_FULLSCREEN); 10470bbfda8aSnia CHKNRET(STATE_SHADED); 10480bbfda8aSnia CHKNRET(STATE_ABOVE); 10490bbfda8aSnia CHKNRET(STATE_BELOW); 10500bbfda8aSnia 10510bbfda8aSnia#undef CHKNRET 10520bbfda8aSnia#undef CRWARN 10530bbfda8aSnia 10540bbfda8aSnia /* Else we don't recognize it */ 10550bbfda8aSnia return 0; 10560bbfda8aSnia} 10570bbfda8aSnia 10580bbfda8aSnia 10590bbfda8aSnia/* 10600bbfda8aSnia * Handle the NET_WM_STATE client message. 10610bbfda8aSnia * It can change 1 or 2 values represented in NET_WM_STATE. 10620bbfda8aSnia * The second change is allowed 10630bbfda8aSnia * specifically for (re)setting horizontal and vertical maximalisation in 10640bbfda8aSnia * one go. Treat that as a special case. 10650bbfda8aSnia */ 10660bbfda8aSniastatic void EwmhClientMessage_NET_WM_STATE(XClientMessageEvent *msg) 10670bbfda8aSnia{ 10680bbfda8aSnia Window w = msg->window; 10690bbfda8aSnia TwmWindow *twm_win; 10700bbfda8aSnia int change, change1, change2, newValue; 10710bbfda8aSnia 10720bbfda8aSnia twm_win = GetTwmWindow(w); 10730bbfda8aSnia 10740bbfda8aSnia if(twm_win == NULL) { 10750bbfda8aSnia return; 10760bbfda8aSnia } 10770bbfda8aSnia 10780bbfda8aSnia /* 10790bbfda8aSnia * Due to EWMHIgnore, it's possible to wind up with change1=0 and 10800bbfda8aSnia * change2=something, so swap 'em if that happens. 10810bbfda8aSnia */ 10820bbfda8aSnia change1 = atomToFlag(msg->data.l[1]); 10830bbfda8aSnia change2 = atomToFlag(msg->data.l[2]); 10840bbfda8aSnia if(change1 == 0 && change2 != 0) { 10850bbfda8aSnia change1 = change2; 10860bbfda8aSnia change2 = 0; 10870bbfda8aSnia } 10880bbfda8aSnia change = change1 | change2; 10890bbfda8aSnia 10900bbfda8aSnia switch(msg->data.l[0]) { 10910bbfda8aSnia case NET_WM_STATE_REMOVE: 10920bbfda8aSnia#ifdef DEBUG_EWMH 10930bbfda8aSnia printf("NET_WM_STATE_REMOVE: "); 10940bbfda8aSnia#endif 10950bbfda8aSnia newValue = 0; 10960bbfda8aSnia break; 10970bbfda8aSnia case NET_WM_STATE_ADD: 10980bbfda8aSnia#ifdef DEBUG_EWMH 10990bbfda8aSnia printf("NET_WM_STATE_ADD: "); 11000bbfda8aSnia#endif 11010bbfda8aSnia newValue = change; 11020bbfda8aSnia break; 11030bbfda8aSnia case NET_WM_STATE_TOGGLE: 11040bbfda8aSnia#ifdef DEBUG_EWMH 11050bbfda8aSnia printf("NET_WM_STATE_TOGGLE: "); 11060bbfda8aSnia#endif 11070bbfda8aSnia newValue = ~twm_win->ewmhFlags & change; 11080bbfda8aSnia break; 11090bbfda8aSnia default: 11100bbfda8aSnia#ifdef DEBUG_EWMH 11110bbfda8aSnia printf("invalid operation in NET_WM_STATE: %ld\n", msg->data.l[0]); 11120bbfda8aSnia#endif 11130bbfda8aSnia return; 11140bbfda8aSnia } 11150bbfda8aSnia#ifdef DEBUG_EWMH 11160bbfda8aSnia printf("%s and %s\n", XGetAtomName(dpy, msg->data.l[1]), 11170bbfda8aSnia XGetAtomName(dpy, msg->data.l[2])); 11180bbfda8aSnia#endif 11190bbfda8aSnia 11200bbfda8aSnia /* 11210bbfda8aSnia * Special-case the horizontal and vertical zoom. 11220bbfda8aSnia * You can turn them both on or both off, but no other combinations 11230bbfda8aSnia * are done as a unit. 11240bbfda8aSnia */ 11250bbfda8aSnia if(change == (EWMH_STATE_MAXIMIZED_VERT | EWMH_STATE_MAXIMIZED_HORZ) && 11260bbfda8aSnia (newValue == 0 || newValue == change)) { 11270bbfda8aSnia EwmhClientMessage_NET_WM_STATEchange(twm_win, change, newValue); 11280bbfda8aSnia } 11290bbfda8aSnia else { 11300bbfda8aSnia EwmhClientMessage_NET_WM_STATEchange(twm_win, change1, newValue & change1); 11310bbfda8aSnia if(change2 != 0) { 11320bbfda8aSnia EwmhClientMessage_NET_WM_STATEchange(twm_win, change2, newValue & change2); 11330bbfda8aSnia } 11340bbfda8aSnia } 11350bbfda8aSnia} 11360bbfda8aSnia 11370bbfda8aSnia/* 11380bbfda8aSnia * change - bitmask of settings that possibly change. Only one bit is 11390bbfda8aSnia * set in this, with the possible exception of 11400bbfda8aSnia * *MAXIMIZED_{VERT,HORIZ} which can be set together. 11410bbfda8aSnia * newValue - bits with the new values; only valid bits are the ones 11420bbfda8aSnia * in change. 11430bbfda8aSnia */ 11440bbfda8aSniastatic void EwmhClientMessage_NET_WM_STATEchange(TwmWindow *twm_win, int change, 11450bbfda8aSnia int newValue) 11460bbfda8aSnia{ 11470bbfda8aSnia /* Now check what we need to change */ 11480bbfda8aSnia 11490bbfda8aSnia if(change & (EWMH_STATE_MAXIMIZED_VERT | EWMH_STATE_MAXIMIZED_HORZ | 11500bbfda8aSnia EWMH_STATE_FULLSCREEN)) { 11510bbfda8aSnia int newZoom = ZOOM_NONE; 11520bbfda8aSnia 11530bbfda8aSnia switch(newValue) { 11540bbfda8aSnia case 0: 11550bbfda8aSnia newZoom = twm_win->zoomed; /* turn off whatever zoom */ 11560bbfda8aSnia break; 11570bbfda8aSnia case EWMH_STATE_MAXIMIZED_VERT: 11580bbfda8aSnia newZoom = F_ZOOM; 11590bbfda8aSnia break; 11600bbfda8aSnia case EWMH_STATE_MAXIMIZED_HORZ: 11610bbfda8aSnia newZoom = F_HORIZOOM; 11620bbfda8aSnia break; 11630bbfda8aSnia case EWMH_STATE_MAXIMIZED_HORZ | EWMH_STATE_MAXIMIZED_VERT: 11640bbfda8aSnia newZoom = F_FULLZOOM; 11650bbfda8aSnia break; 11660bbfda8aSnia case EWMH_STATE_FULLSCREEN: 11670bbfda8aSnia newZoom = F_FULLSCREENZOOM; 11680bbfda8aSnia break; 11690bbfda8aSnia } 11700bbfda8aSnia fullzoom(twm_win, newZoom); 11710bbfda8aSnia } 11720bbfda8aSnia else if(change & EWMH_STATE_SHADED) { 11730bbfda8aSnia#ifdef DEBUG_EWMH 11740bbfda8aSnia printf("EWMH_STATE_SHADED: newValue: %d old: %d\n", newValue, 11750bbfda8aSnia twm_win->ewmhFlags & EWMH_STATE_SHADED); 11760bbfda8aSnia#endif 11770bbfda8aSnia if((twm_win->ewmhFlags & EWMH_STATE_SHADED) ^ newValue) { 11780bbfda8aSnia /* Toggle the shade/squeeze state */ 11790bbfda8aSnia#ifdef DEBUG_EWMH 11800bbfda8aSnia printf("EWMH_STATE_SHADED: change it\n"); 11810bbfda8aSnia#endif 11820bbfda8aSnia Squeeze(twm_win); 11830bbfda8aSnia } 11840bbfda8aSnia } 11850bbfda8aSnia else if(change & (EWMH_STATE_ABOVE | EWMH_STATE_BELOW)) { 11860bbfda8aSnia /* 11870bbfda8aSnia * Other changes call into ctwm code, which in turn calls back to 11880bbfda8aSnia * EWMH code to update the ewmhFlags and the property. This one 11890bbfda8aSnia * we handle completely internally. 11900bbfda8aSnia */ 11910bbfda8aSnia unsigned omask = 0, oval = 0; 11920bbfda8aSnia const int prepri = OtpEffectivePriority(twm_win); 11930bbfda8aSnia 11940bbfda8aSnia /* Which bits are we changing and what to? */ 11950bbfda8aSnia#define DOBIT(fld) do { \ 11960bbfda8aSnia if(change & EWMH_STATE_##fld) { omask |= OTP_AFLAG_##fld; } \ 11970bbfda8aSnia if(newValue & EWMH_STATE_##fld) { oval |= OTP_AFLAG_##fld; } \ 11980bbfda8aSnia } while(0) 11990bbfda8aSnia 12000bbfda8aSnia DOBIT(ABOVE); 12010bbfda8aSnia DOBIT(BELOW); 12020bbfda8aSnia 12030bbfda8aSnia#undef DOBIT 12040bbfda8aSnia 12050bbfda8aSnia /* Update OTP as necessary */ 12060bbfda8aSnia OtpSetAflagMask(twm_win, omask, oval); 12070bbfda8aSnia if(OtpEffectivePriority(twm_win) != prepri) { 12080bbfda8aSnia OtpRestackWindow(twm_win); 12090bbfda8aSnia } 12100bbfda8aSnia 12110bbfda8aSnia /* Set the EWMH property back on the window */ 12120bbfda8aSnia EwmhSet_NET_WM_STATE(twm_win, change); 12130bbfda8aSnia } 12140bbfda8aSnia} 12150bbfda8aSnia 12160bbfda8aSnia/* 12170bbfda8aSnia * Handle the _NET_ACTIVE_WINDOW client message. 12180bbfda8aSnia * Pagers would send such a message to "activate" a window. 12190bbfda8aSnia * 12200bbfda8aSnia * What does "activate" really mean? It isn't properly described. 12210bbfda8aSnia * 12220bbfda8aSnia * Let's presume that it means that the window is de-iconified and gets 12230bbfda8aSnia * focus. The mouse may be moved to it (but not all window button apps 12240bbfda8aSnia * do that). But is it always raised or should that depend on the 12250bbfda8aSnia * RaiseOnWarp option? 12260bbfda8aSnia */ 12270bbfda8aSniastatic void EwmhClientMessage_NET_ACTIVE_WINDOW(XClientMessageEvent *msg) 12280bbfda8aSnia{ 12290bbfda8aSnia Window w = msg->window; 12300bbfda8aSnia TwmWindow *twm_win; 12310bbfda8aSnia 12320bbfda8aSnia twm_win = GetTwmWindow(w); 12330bbfda8aSnia 12340bbfda8aSnia if(twm_win == NULL) { 12350bbfda8aSnia return; 12360bbfda8aSnia } 12370bbfda8aSnia 12380bbfda8aSnia if(!twm_win->mapped) { 12390bbfda8aSnia DeIconify(twm_win); 12400bbfda8aSnia } 12410bbfda8aSnia#if 0 12420bbfda8aSnia WarpToWindow(twm_win, Scr->RaiseOnWarp /* True ? */); 12430bbfda8aSnia#else 12440bbfda8aSnia /* 12450bbfda8aSnia * Keep the mouse pointer where it is (typically the dock). 12460bbfda8aSnia * WarpToWindow() would change the current workspace if needed to go 12470bbfda8aSnia * to the window. But pagers would only send this message for 12480bbfda8aSnia * windows in the current workspace, I expect. 12490bbfda8aSnia */ 12500bbfda8aSnia if(Scr->RaiseOnWarp) { 12510bbfda8aSnia AutoRaiseWindow(twm_win); 12520bbfda8aSnia } 12530bbfda8aSnia SetFocus(twm_win, msg->data.l[1]); 12540bbfda8aSnia#endif 12550bbfda8aSnia} 12560bbfda8aSnia 12570bbfda8aSnia/* 12580bbfda8aSnia * Ugly implementation of _NET_WM_MOVERESIZE. 12590bbfda8aSnia * 12600bbfda8aSnia * window = window to be moved or resized 12610bbfda8aSnia * message_type = _NET_WM_MOVERESIZE 12620bbfda8aSnia * format = 32 12630bbfda8aSnia * data.l[0] = x_root 12640bbfda8aSnia * data.l[1] = y_root 12650bbfda8aSnia * data.l[2] = direction 12660bbfda8aSnia * data.l[3] = button 12670bbfda8aSnia * data.l[4] = source indication 12680bbfda8aSnia */ 12690bbfda8aSniastatic void EwmhClientMessage_NET_WM_MOVERESIZE(XClientMessageEvent *msg) 12700bbfda8aSnia{ 12710bbfda8aSnia TwmWindow *twm_win; 12720bbfda8aSnia 12730bbfda8aSnia twm_win = GetTwmWindow(msg->window); 12740bbfda8aSnia 12750bbfda8aSnia if(twm_win == NULL) { 12760bbfda8aSnia return; 12770bbfda8aSnia } 12780bbfda8aSnia 12790bbfda8aSnia if(!twm_win->mapped) { 12800bbfda8aSnia DeIconify(twm_win); 12810bbfda8aSnia } 12820bbfda8aSnia 12830bbfda8aSnia switch(msg->data.l[2]) { 12840bbfda8aSnia case _NET_WM_MOVERESIZE_SIZE_TOPLEFT: 12850bbfda8aSnia case _NET_WM_MOVERESIZE_SIZE_TOP: 12860bbfda8aSnia case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT: 12870bbfda8aSnia case _NET_WM_MOVERESIZE_SIZE_RIGHT: 12880bbfda8aSnia case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT: 12890bbfda8aSnia case _NET_WM_MOVERESIZE_SIZE_BOTTOM: 12900bbfda8aSnia case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT: 12910bbfda8aSnia case _NET_WM_MOVERESIZE_SIZE_LEFT: 12920bbfda8aSnia case _NET_WM_MOVERESIZE_SIZE_KEYBOARD: { 12930bbfda8aSnia /* Fake up as if we triggered f.resize on it */ 12940bbfda8aSnia XEvent xevent = synth_btnevent_for_moveresize(twm_win); 12950bbfda8aSnia 12960bbfda8aSnia /* 12970bbfda8aSnia * The resize won't actually start until we cross the cursor 12980bbfda8aSnia * over a border. Perhaps we should find the nearest corner, 12990bbfda8aSnia * and pre-warp the cursor there? That may be less friendly 13000bbfda8aSnia * for the user, since it might not be as predictable, and 13010bbfda8aSnia * having the cursor zoom off without warning is probably a 13020bbfda8aSnia * little surprising... 13030bbfda8aSnia */ 13040bbfda8aSnia ExecuteFunction(F_RESIZE, "", twm_win->frame, twm_win, 13050bbfda8aSnia &xevent, C_WINDOW, false); 13060bbfda8aSnia 13070bbfda8aSnia /* 13080bbfda8aSnia * It's not guaranteed that this actually began as a button 13090bbfda8aSnia * press, but our implementation is expecting the button to 13100bbfda8aSnia * be held down all through the move, so that 13110bbfda8aSnia * ExecuteFunction() won't actually end until there's a 13120bbfda8aSnia * button release of some kind, which will trigger 13130bbfda8aSnia * HandleButtonRelease() properly. So we don't need to call 13140bbfda8aSnia * it ourselves. 13150bbfda8aSnia */ 13160bbfda8aSnia 13170bbfda8aSnia break; 13180bbfda8aSnia } 13190bbfda8aSnia case _NET_WM_MOVERESIZE_MOVE: 13200bbfda8aSnia case _NET_WM_MOVERESIZE_MOVE_KEYBOARD: { 13210bbfda8aSnia /* Fake up as if we triggered f.move on it */ 13220bbfda8aSnia XEvent xevent = synth_btnevent_for_moveresize(twm_win); 13230bbfda8aSnia ExecuteFunction(F_MOVE, "", twm_win->frame, twm_win, 13240bbfda8aSnia &xevent, C_WINDOW, false); 13250bbfda8aSnia 13260bbfda8aSnia /* X-ref HandleButtonRelease() discussion above */ 13270bbfda8aSnia break; 13280bbfda8aSnia } 13290bbfda8aSnia case _NET_WM_MOVERESIZE_CANCEL: 13300bbfda8aSnia /* 13310bbfda8aSnia * TODO: check if the twm_win is the same. 13320bbfda8aSnia * TODO: check how to make this actually work. 13330bbfda8aSnia * 13340bbfda8aSnia * As currently implemented, I don't believe we ever need to 13350bbfda8aSnia * do anything here. All the needed cleanup should happen in 13360bbfda8aSnia * our ButtonRelease handler. 13370bbfda8aSnia */ 13380bbfda8aSnia break; 13390bbfda8aSnia } 13400bbfda8aSnia} 13410bbfda8aSnia 13420bbfda8aSniastatic XEvent 13430bbfda8aSniasynth_btnevent_for_moveresize(TwmWindow *twm_win) 13440bbfda8aSnia{ 13450bbfda8aSnia XEvent xevent; 13460bbfda8aSnia Window root = twm_win->parent_vs->window; 13470bbfda8aSnia Window child = twm_win->w; 13480bbfda8aSnia int x_root = twm_win->frame_x; 13490bbfda8aSnia int y_root = twm_win->frame_y; 13500bbfda8aSnia int x_win = 0; 13510bbfda8aSnia int y_win = 0; 13520bbfda8aSnia unsigned int dummy_mask; 13530bbfda8aSnia 13540bbfda8aSnia /* Find the pointer */ 13550bbfda8aSnia XQueryPointer(dpy, twm_win->frame, &root, &child, &x_root, &y_root, 13560bbfda8aSnia &x_win, &y_win, &dummy_mask); 13570bbfda8aSnia 13580bbfda8aSnia /* Synthesize a button event */ 13590bbfda8aSnia xevent.type = ButtonPress; 13600bbfda8aSnia xevent.xbutton.root = root; 13610bbfda8aSnia xevent.xbutton.window = child; 13620bbfda8aSnia xevent.xbutton.x_root = x_root; 13630bbfda8aSnia xevent.xbutton.y_root = y_root; 13640bbfda8aSnia xevent.xbutton.x = x_win; 13650bbfda8aSnia xevent.xbutton.y = y_win; 13660bbfda8aSnia xevent.xbutton.time = EventTime; 13670bbfda8aSnia 13680bbfda8aSnia return xevent; 13690bbfda8aSnia} 13700bbfda8aSnia 13710bbfda8aSnia 1372b18c2d1eSnia/* 1373b18c2d1eSnia * Implementation of _NET_CLOSE_WINDOW 1374b18c2d1eSnia * 1375b18c2d1eSnia * window = window to be closed 1376b18c2d1eSnia * message_type = _NET_CLOSE_WINDOW 1377b18c2d1eSnia * format = 32 1378b18c2d1eSnia * data.l[0] = timestamp 1379b18c2d1eSnia * data.l[1] = source indication: 1380b18c2d1eSnia * 0 Clients that support only older version of this spec 1381b18c2d1eSnia * 1 for normal applications, and 1382b18c2d1eSnia * 2 for pagers and other Clients that represent direct user actions 1383b18c2d1eSnia * data.l[2] = 0 1384b18c2d1eSnia * data.l[3] = 0 1385b18c2d1eSnia * data.l[4] = 0 1386b18c2d1eSnia */ 1387b18c2d1eSniastatic void EwmhClientMessage_NET_CLOSE_WINDOW(XClientMessageEvent *msg) 1388b18c2d1eSnia{ 1389b18c2d1eSnia TwmWindow *tmp_win; 1390b18c2d1eSnia 1391b18c2d1eSnia tmp_win = GetTwmWindow(msg->window); 1392b18c2d1eSnia 1393b18c2d1eSnia if(tmp_win != NULL) { 1394b18c2d1eSnia ButtonPressed = -1; 1395b18c2d1eSnia ExecuteFunction(F_DELETE, NULL, msg->window, tmp_win, 1396b18c2d1eSnia (XEvent *)msg, C_NO_CONTEXT, 0); 1397b18c2d1eSnia } 1398b18c2d1eSnia} 1399b18c2d1eSnia 14000bbfda8aSnia/* 14010bbfda8aSnia * Handle any PropertyNotify. 14020bbfda8aSnia */ 14030bbfda8aSniaint EwmhHandlePropertyNotify(XPropertyEvent *event, TwmWindow *twm_win) 14040bbfda8aSnia{ 14050bbfda8aSnia if(event->atom == XA__NET_WM_ICON) { 14060bbfda8aSnia EwmhHandle_NET_WM_ICONNotify(event, twm_win); 14070bbfda8aSnia return 1; 14080bbfda8aSnia } 14090bbfda8aSnia else if(event->atom == XA__NET_WM_STRUT_PARTIAL || 14100bbfda8aSnia event->atom == XA__NET_WM_STRUT) { 14110bbfda8aSnia EwmhHandle_NET_WM_STRUTNotify(event, twm_win); 14120bbfda8aSnia return 1; 14130bbfda8aSnia } 14140bbfda8aSnia else if(event->atom == XA__NET_WM_NAME) { 14150bbfda8aSnia char *prop = GetWMPropertyString(twm_win->w, XA__NET_WM_NAME); 14160bbfda8aSnia if(prop == NULL) { 14170bbfda8aSnia FreeWMPropertyString(twm_win->names.net_wm_name); 14180bbfda8aSnia twm_win->names.net_wm_name = NULL; 14190bbfda8aSnia apply_window_name(twm_win); 14200bbfda8aSnia return 1; 14210bbfda8aSnia } 14220bbfda8aSnia 14230bbfda8aSnia if(twm_win->names.net_wm_name != NULL 14240bbfda8aSnia && strcmp(twm_win->names.net_wm_name, prop) == 0) { 14250bbfda8aSnia /* No change, just free and skip out */ 14260bbfda8aSnia free(prop); 14270bbfda8aSnia return 1; 14280bbfda8aSnia } 14290bbfda8aSnia 14300bbfda8aSnia /* It's changing, free the old and bring in the new */ 14310bbfda8aSnia FreeWMPropertyString(twm_win->names.net_wm_name); 14320bbfda8aSnia twm_win->names.net_wm_name = prop; 14330bbfda8aSnia 14340bbfda8aSnia /* Kick the reset process */ 14350bbfda8aSnia apply_window_name(twm_win); 14360bbfda8aSnia return 1; 14370bbfda8aSnia } 14380bbfda8aSnia else if(event->atom == XA__NET_WM_ICON_NAME) { 14390bbfda8aSnia char *prop = GetWMPropertyString(twm_win->w, XA__NET_WM_ICON_NAME); 14400bbfda8aSnia if(prop == NULL) { 14410bbfda8aSnia FreeWMPropertyString(twm_win->names.net_wm_icon_name); 14420bbfda8aSnia twm_win->names.net_wm_icon_name = NULL; 14430bbfda8aSnia apply_window_icon_name(twm_win); 14440bbfda8aSnia return 1; 14450bbfda8aSnia } 14460bbfda8aSnia 14470bbfda8aSnia if(twm_win->names.net_wm_icon_name != NULL 14480bbfda8aSnia && strcmp(twm_win->names.net_wm_icon_name, prop) == 0) { 14490bbfda8aSnia /* No change, just free and skip out */ 14500bbfda8aSnia free(prop); 14510bbfda8aSnia return 1; 14520bbfda8aSnia } 14530bbfda8aSnia 14540bbfda8aSnia /* It's changing, free the old and bring in the new */ 14550bbfda8aSnia FreeWMPropertyString(twm_win->names.net_wm_icon_name); 14560bbfda8aSnia twm_win->names.net_wm_icon_name = prop; 14570bbfda8aSnia 14580bbfda8aSnia /* Kick the reset process */ 14590bbfda8aSnia apply_window_icon_name(twm_win); 14600bbfda8aSnia return 1; 14610bbfda8aSnia } 14620bbfda8aSnia 14630bbfda8aSnia return 0; 14640bbfda8aSnia} 14650bbfda8aSnia 14660bbfda8aSnia/* 14670bbfda8aSnia * Set the _NET_WM_DESKTOP property for the current workspace. 14680bbfda8aSnia */ 14690bbfda8aSniavoid EwmhSet_NET_WM_DESKTOP(TwmWindow *twm_win) 14700bbfda8aSnia{ 14710bbfda8aSnia WorkSpace *ws; 14720bbfda8aSnia 14730bbfda8aSnia VirtualScreen *vs = twm_win->vs; 14740bbfda8aSnia if(vs != NULL) { 14750bbfda8aSnia ws = vs->wsw->currentwspc; 14760bbfda8aSnia } 14770bbfda8aSnia else { 14780bbfda8aSnia ws = NULL; 14790bbfda8aSnia } 14800bbfda8aSnia 14810bbfda8aSnia EwmhSet_NET_WM_DESKTOP_ws(twm_win, ws); 14820bbfda8aSnia} 14830bbfda8aSnia 14840bbfda8aSnia/* 14850bbfda8aSnia * Set the _NET_WM_DESKTOP property for the given workspace. 14860bbfda8aSnia */ 14870bbfda8aSniavoid EwmhSet_NET_WM_DESKTOP_ws(TwmWindow *twm_win, WorkSpace *ws) 14880bbfda8aSnia{ 14890bbfda8aSnia unsigned long workspaces[MAXWORKSPACE]; 14900bbfda8aSnia int n = 0; 14910bbfda8aSnia 14920bbfda8aSnia if(!Scr->workSpaceManagerActive) { 14930bbfda8aSnia workspaces[n++] = 0; 14940bbfda8aSnia } 14950bbfda8aSnia else if(twm_win->occupation == fullOccupation) { 14960bbfda8aSnia workspaces[n++] = ALL_WORKSPACES; 14970bbfda8aSnia } 14980bbfda8aSnia else { 14990bbfda8aSnia /* 15000bbfda8aSnia * Our windows can occupy multiple workspaces ("virtual desktops" in 15010bbfda8aSnia * EWMH terminology) at once. Extend the _NET_WM_DESKTOP property 15020bbfda8aSnia * by setting it to multiple CARDINALs if this occurs. 15030bbfda8aSnia * Put the currently visible workspace (if any) first, since typical 15040bbfda8aSnia * pager apps don't know about this. 15050bbfda8aSnia */ 15060bbfda8aSnia int occupation = twm_win->occupation; 15070bbfda8aSnia 15080bbfda8aSnia /* 15090bbfda8aSnia * Set visible workspace number. 15100bbfda8aSnia */ 15110bbfda8aSnia if(ws != NULL) { 15120bbfda8aSnia int wsn = ws->number; 15130bbfda8aSnia 15140bbfda8aSnia workspaces[n++] = wsn; 15150bbfda8aSnia occupation &= ~(1 << wsn); 15160bbfda8aSnia } 15170bbfda8aSnia 15180bbfda8aSnia /* 15190bbfda8aSnia * Set any other workspace numbers. 15200bbfda8aSnia */ 15210bbfda8aSnia if(occupation != 0) { 15220bbfda8aSnia int i; 15230bbfda8aSnia int mask = 1; 15240bbfda8aSnia 15250bbfda8aSnia for(i = 0; i < MAXWORKSPACE; i++) { 15260bbfda8aSnia if(occupation & mask) { 15270bbfda8aSnia workspaces[n++] = i; 15280bbfda8aSnia } 15290bbfda8aSnia mask <<= 1; 15300bbfda8aSnia } 15310bbfda8aSnia } 15320bbfda8aSnia } 15330bbfda8aSnia 15340bbfda8aSnia XChangeProperty(dpy, twm_win->w, 15350bbfda8aSnia XA__NET_WM_DESKTOP, XA_CARDINAL, 15360bbfda8aSnia 32, PropModeReplace, 15370bbfda8aSnia (unsigned char *)workspaces, n); 15380bbfda8aSnia} 15390bbfda8aSnia 15400bbfda8aSnia 15410bbfda8aSniastatic unsigned long EwmhGetWindowProperty(Window w, Atom name, Atom type) 15420bbfda8aSnia{ 15430bbfda8aSnia Atom actual_type; 15440bbfda8aSnia int actual_format; 15450bbfda8aSnia unsigned long nitems, bytes_after; 15460bbfda8aSnia unsigned long *prop; 15470bbfda8aSnia unsigned long value; 15480bbfda8aSnia 15490bbfda8aSnia if(XGetWindowProperty(dpy, w, name, 15500bbfda8aSnia 0, 1, False, type, 15510bbfda8aSnia &actual_type, &actual_format, &nitems, 15520bbfda8aSnia &bytes_after, (unsigned char **)&prop) != Success) { 15530bbfda8aSnia return 0; 15540bbfda8aSnia } 15550bbfda8aSnia 15560bbfda8aSnia if(actual_format == 32) { 15570bbfda8aSnia if(nitems >= 1) { 15580bbfda8aSnia value = prop[0]; 15590bbfda8aSnia } 15600bbfda8aSnia else { 15610bbfda8aSnia value = 0; 15620bbfda8aSnia } 15630bbfda8aSnia } 15640bbfda8aSnia else { 15650bbfda8aSnia value = 0; 15660bbfda8aSnia } 15670bbfda8aSnia 15680bbfda8aSnia XFree(prop); 15690bbfda8aSnia return value; 15700bbfda8aSnia} 15710bbfda8aSnia 15720bbfda8aSnia/* 15730bbfda8aSnia * Simple function to get multiple properties of format 32. 15740bbfda8aSnia * If it fails, returns NULL, and *nitems_return == 0. 15750bbfda8aSnia */ 15760bbfda8aSnia 15770bbfda8aSniastatic unsigned long *EwmhGetWindowProperties(Window w, Atom name, Atom type, 15780bbfda8aSnia unsigned long *nitems_return) 15790bbfda8aSnia{ 15800bbfda8aSnia Atom actual_type; 15810bbfda8aSnia int actual_format; 15820bbfda8aSnia unsigned long bytes_after; 15830bbfda8aSnia unsigned long *prop; 15840bbfda8aSnia 15850bbfda8aSnia if(XGetWindowProperty(dpy, w, name, 15860bbfda8aSnia 0, 8192, False, type, 15870bbfda8aSnia &actual_type, &actual_format, nitems_return, 15880bbfda8aSnia &bytes_after, (unsigned char **)&prop) != Success) { 15890bbfda8aSnia *nitems_return = 0; 15900bbfda8aSnia return NULL; 15910bbfda8aSnia } 15920bbfda8aSnia 15930bbfda8aSnia if(actual_format != 32) { 15940bbfda8aSnia XFree(prop); 15950bbfda8aSnia prop = NULL; 15960bbfda8aSnia *nitems_return = 0; 15970bbfda8aSnia } 15980bbfda8aSnia 15990bbfda8aSnia return prop; 16000bbfda8aSnia} 16010bbfda8aSnia 16020bbfda8aSniaint EwmhGetOccupation(TwmWindow *twm_win) 16030bbfda8aSnia{ 16040bbfda8aSnia unsigned long nitems; 16050bbfda8aSnia unsigned long *prop; 16060bbfda8aSnia int occupation; 16070bbfda8aSnia 16080bbfda8aSnia occupation = 0; 16090bbfda8aSnia 16100bbfda8aSnia prop = EwmhGetWindowProperties(twm_win->w, 16110bbfda8aSnia XA__NET_WM_DESKTOP, XA_CARDINAL, &nitems); 16120bbfda8aSnia 16130bbfda8aSnia if(prop) { 16140bbfda8aSnia int i; 16150bbfda8aSnia for(i = 0; i < nitems; i++) { 16160bbfda8aSnia unsigned int val = prop[i]; 16170bbfda8aSnia if(val == ALL_WORKSPACES) { 16180bbfda8aSnia occupation = fullOccupation; 16190bbfda8aSnia } 16200bbfda8aSnia else if(val < Scr->workSpaceMgr.count) { 16210bbfda8aSnia occupation |= 1 << val; 16220bbfda8aSnia } 16230bbfda8aSnia else { 16240bbfda8aSnia occupation |= 1 << (Scr->workSpaceMgr.count - 1); 16250bbfda8aSnia } 16260bbfda8aSnia } 16270bbfda8aSnia 16280bbfda8aSnia occupation &= fullOccupation; 16290bbfda8aSnia 16300bbfda8aSnia XFree(prop); 16310bbfda8aSnia } 16320bbfda8aSnia 16330bbfda8aSnia return occupation; 16340bbfda8aSnia} 16350bbfda8aSnia 16360bbfda8aSnia/* 16370bbfda8aSnia * The message to change the desktop of a window doesn't recognize 16380bbfda8aSnia * that it may be in more than one. 16390bbfda8aSnia * 16400bbfda8aSnia * Therefore the following heuristic is applied: 16410bbfda8aSnia * - the window is removed from the workspace where it is visible (if any); 16420bbfda8aSnia * - it is added to the given workspace; 16430bbfda8aSnia * - other occupation bits are left unchanged. 16440bbfda8aSnia * 16450bbfda8aSnia * If asked to put it on a too high numbered workspace, put it on 16460bbfda8aSnia * the highest possible. 16470bbfda8aSnia */ 16480bbfda8aSniastatic void EwmhClientMessage_NET_WM_DESKTOP(XClientMessageEvent *msg) 16490bbfda8aSnia{ 16500bbfda8aSnia Window w = msg->window; 16510bbfda8aSnia TwmWindow *twm_win; 16520bbfda8aSnia int occupation; 16530bbfda8aSnia VirtualScreen *vs; 16540bbfda8aSnia unsigned int val; 16550bbfda8aSnia 16560bbfda8aSnia twm_win = GetTwmWindow(w); 16570bbfda8aSnia 16580bbfda8aSnia if(twm_win == NULL) { 16590bbfda8aSnia return; 16600bbfda8aSnia } 16610bbfda8aSnia 16620bbfda8aSnia occupation = twm_win->occupation; 16630bbfda8aSnia 16640bbfda8aSnia /* Remove from visible workspace */ 16650bbfda8aSnia if((vs = twm_win->vs) != NULL) { 16660bbfda8aSnia occupation &= ~(1 << vs->wsw->currentwspc->number); 16670bbfda8aSnia } 16680bbfda8aSnia 16690bbfda8aSnia val = (unsigned int)msg->data.l[0]; 16700bbfda8aSnia 16710bbfda8aSnia /* Add to requested workspace (or to all) */ 16720bbfda8aSnia if(val == ALL_WORKSPACES) { 16730bbfda8aSnia occupation = fullOccupation; 16740bbfda8aSnia } 16750bbfda8aSnia else if(val < Scr->workSpaceMgr.count) { 16760bbfda8aSnia occupation |= 1 << val; 16770bbfda8aSnia } 16780bbfda8aSnia else { 16790bbfda8aSnia occupation |= 1 << (Scr->workSpaceMgr.count - 1); 16800bbfda8aSnia } 16810bbfda8aSnia 16820bbfda8aSnia ChangeOccupation(twm_win, occupation); 16830bbfda8aSnia} 16840bbfda8aSnia 16850bbfda8aSnia/* 16860bbfda8aSnia * Delete all properties that should be removed from a withdrawn 16870bbfda8aSnia * window. 16880bbfda8aSnia */ 16890bbfda8aSniavoid EwmhUnmapNotify(TwmWindow *twm_win) 16900bbfda8aSnia{ 16910bbfda8aSnia XDeleteProperty(dpy, twm_win->w, XA__NET_WM_DESKTOP); 16920bbfda8aSnia} 16930bbfda8aSnia 16940bbfda8aSnia/* 16950bbfda8aSnia * Add a new window to _NET_CLIENT_LIST. 16960bbfda8aSnia * Newer windows are always added at the end. 16970bbfda8aSnia * 16980bbfda8aSnia * Look at new_win->iconmanagerlist as an optimization for 16990bbfda8aSnia * !LookInList(Scr->IconMgrNoShow, new_win->name, &new_win->class)). 17000bbfda8aSnia */ 17010bbfda8aSniavoid EwmhAddClientWindow(TwmWindow *new_win) 17020bbfda8aSnia{ 17030bbfda8aSnia if(Scr->ewmh_CLIENT_LIST_size == 0) { 17040bbfda8aSnia return; 17050bbfda8aSnia } 17060bbfda8aSnia if(new_win->iconmanagerlist != NULL && 17070bbfda8aSnia !new_win->iswspmgr && 17080bbfda8aSnia !new_win->isiconmgr) { 17090bbfda8aSnia Scr->ewmh_CLIENT_LIST_used++; 17100bbfda8aSnia if(Scr->ewmh_CLIENT_LIST_used > Scr->ewmh_CLIENT_LIST_size) { 17110bbfda8aSnia long *tp; 17120bbfda8aSnia int tsz = Scr->ewmh_CLIENT_LIST_size; 17130bbfda8aSnia 17140bbfda8aSnia Scr->ewmh_CLIENT_LIST_size *= 2; 17150bbfda8aSnia tp = realloc(Scr->ewmh_CLIENT_LIST, 17160bbfda8aSnia sizeof(long) * Scr->ewmh_CLIENT_LIST_size); 17170bbfda8aSnia if(tp == NULL) { 17180bbfda8aSnia Scr->ewmh_CLIENT_LIST_size = tsz; 17190bbfda8aSnia fprintf(stderr, "Unable to allocate memory for EWMH client list.\n"); 17200bbfda8aSnia return; 17210bbfda8aSnia } 17220bbfda8aSnia Scr->ewmh_CLIENT_LIST = tp; 17230bbfda8aSnia } 17240bbfda8aSnia if(Scr->ewmh_CLIENT_LIST) { 17250bbfda8aSnia Scr->ewmh_CLIENT_LIST[Scr->ewmh_CLIENT_LIST_used - 1] = new_win->w; 17260bbfda8aSnia } 17270bbfda8aSnia else { 17280bbfda8aSnia Scr->ewmh_CLIENT_LIST_size = 0; 17290bbfda8aSnia fprintf(stderr, "Unable to allocate memory for EWMH client list.\n"); 17300bbfda8aSnia return; 17310bbfda8aSnia } 17320bbfda8aSnia XChangeProperty(dpy, Scr->Root, XA__NET_CLIENT_LIST, XA_WINDOW, 32, 17330bbfda8aSnia PropModeReplace, (unsigned char *)Scr->ewmh_CLIENT_LIST, 17340bbfda8aSnia Scr->ewmh_CLIENT_LIST_used); 17350bbfda8aSnia } 17360bbfda8aSnia} 17370bbfda8aSnia 17380bbfda8aSniavoid EwmhDeleteClientWindow(TwmWindow *old_win) 17390bbfda8aSnia{ 17400bbfda8aSnia int i; 17410bbfda8aSnia 17420bbfda8aSnia if(old_win->ewmhFlags & EWMH_HAS_STRUT) { 17430bbfda8aSnia EwmhRemoveStrut(old_win); 17440bbfda8aSnia } 17450bbfda8aSnia 17460bbfda8aSnia /* 17470bbfda8aSnia * Remove the window from _NET_CLIENT_LIST. 17480bbfda8aSnia */ 17490bbfda8aSnia if(Scr->ewmh_CLIENT_LIST_size == 0) { 17500bbfda8aSnia return; 17510bbfda8aSnia } 17520bbfda8aSnia for(i = Scr->ewmh_CLIENT_LIST_used - 1; i >= 0; i--) { 17530bbfda8aSnia if(Scr->ewmh_CLIENT_LIST[i] == old_win->w) { 17540bbfda8aSnia memmove(&Scr->ewmh_CLIENT_LIST[i], 17550bbfda8aSnia &Scr->ewmh_CLIENT_LIST[i + 1], 17560bbfda8aSnia (Scr->ewmh_CLIENT_LIST_used - 1 - i) * sizeof(Scr->ewmh_CLIENT_LIST[0])); 17570bbfda8aSnia Scr->ewmh_CLIENT_LIST_used--; 17580bbfda8aSnia if(Scr->ewmh_CLIENT_LIST_used && 17590bbfda8aSnia (Scr->ewmh_CLIENT_LIST_used * 3) < Scr->ewmh_CLIENT_LIST_size) { 17600bbfda8aSnia Scr->ewmh_CLIENT_LIST_size /= 2; 17610bbfda8aSnia Scr->ewmh_CLIENT_LIST = realloc(Scr->ewmh_CLIENT_LIST, 17620bbfda8aSnia sizeof((Scr->ewmh_CLIENT_LIST[0])) * Scr->ewmh_CLIENT_LIST_size); 17630bbfda8aSnia /* memory shrinking, shouldn't have problems */ 17640bbfda8aSnia } 17650bbfda8aSnia break; 17660bbfda8aSnia } 17670bbfda8aSnia } 17680bbfda8aSnia /* If window was not found, there is no need to update the property. */ 17690bbfda8aSnia if(i >= 0) { 17700bbfda8aSnia XChangeProperty(dpy, Scr->Root, XA__NET_CLIENT_LIST, XA_WINDOW, 32, 17710bbfda8aSnia PropModeReplace, (unsigned char *)Scr->ewmh_CLIENT_LIST, 17720bbfda8aSnia Scr->ewmh_CLIENT_LIST_used); 17730bbfda8aSnia } 17740bbfda8aSnia} 17750bbfda8aSnia 17760bbfda8aSnia/* 17770bbfda8aSnia * Similar to EwmhAddClientWindow() and EwmhDeleteClientWindow(), 17780bbfda8aSnia * but the windows are in stacking order. 17790bbfda8aSnia * Therefore we look at the OTPs, which are by definition in the correct order. 17800bbfda8aSnia */ 17810bbfda8aSnia 17820bbfda8aSniavoid EwmhSet_NET_CLIENT_LIST_STACKING(void) 17830bbfda8aSnia{ 17840bbfda8aSnia int size; 17850bbfda8aSnia unsigned long *prop; 17860bbfda8aSnia TwmWindow *twm_win; 17870bbfda8aSnia int i; 17880bbfda8aSnia 17890bbfda8aSnia /* Expect the same number of windows as in the _NET_CLIENT_LIST */ 17900bbfda8aSnia size = Scr->ewmh_CLIENT_LIST_used + 10; 17910bbfda8aSnia prop = calloc(size, sizeof(unsigned long)); 17920bbfda8aSnia if(prop == NULL) { 17930bbfda8aSnia return; 17940bbfda8aSnia } 17950bbfda8aSnia 17960bbfda8aSnia i = 0; 17970bbfda8aSnia for(twm_win = OtpBottomWin(); 17980bbfda8aSnia twm_win != NULL; 17990bbfda8aSnia twm_win = OtpNextWinUp(twm_win)) { 18000bbfda8aSnia if(twm_win->iconmanagerlist != NULL && 18010bbfda8aSnia !twm_win->iswspmgr && 18020bbfda8aSnia !twm_win->isiconmgr) { 18030bbfda8aSnia prop[i] = twm_win->w; 18040bbfda8aSnia i++; 18050bbfda8aSnia if(i > size) { 18060bbfda8aSnia fprintf(stderr, "Too many stacked windows\n"); 18070bbfda8aSnia break; 18080bbfda8aSnia } 18090bbfda8aSnia } 18100bbfda8aSnia } 18110bbfda8aSnia 18120bbfda8aSnia if(i != Scr->ewmh_CLIENT_LIST_used) { 18130bbfda8aSnia fprintf(stderr, "Incorrect number of stacked windows: %d (expected %d)\n", 18140bbfda8aSnia i, Scr->ewmh_CLIENT_LIST_used); 18150bbfda8aSnia } 18160bbfda8aSnia 18170bbfda8aSnia XChangeProperty(dpy, Scr->Root, XA__NET_CLIENT_LIST_STACKING, XA_WINDOW, 32, 18180bbfda8aSnia PropModeReplace, (unsigned char *)prop, i); 18190bbfda8aSnia 18200bbfda8aSnia free(prop); 18210bbfda8aSnia} 18220bbfda8aSnia 18230bbfda8aSniavoid EwmhSet_NET_ACTIVE_WINDOW(Window w) 18240bbfda8aSnia{ 18250bbfda8aSnia unsigned long prop[1]; 18260bbfda8aSnia 18270bbfda8aSnia prop[0] = w; 18280bbfda8aSnia 18290bbfda8aSnia XChangeProperty(dpy, Scr->Root, XA__NET_ACTIVE_WINDOW, XA_WINDOW, 32, 18300bbfda8aSnia PropModeReplace, (unsigned char *)prop, 1); 18310bbfda8aSnia} 18320bbfda8aSnia 18330bbfda8aSnia/* 18340bbfda8aSnia * Get window properties as relevant when the window is initially mapped. 18350bbfda8aSnia * 18360bbfda8aSnia * So far, only NET_WM_WINDOW_TYPE and _NET_WM_STRUT_PARTIAL. 18370bbfda8aSnia * In particular, most of the initial value of _NET_WM_STATE is ignored. TODO. 18380bbfda8aSnia * 18390bbfda8aSnia * Also do any generic initialisation needed to EWMH-specific fields 18400bbfda8aSnia * in a TwmWindow. 18410bbfda8aSnia */ 18420bbfda8aSniavoid EwmhGetProperties(TwmWindow *twm_win) 18430bbfda8aSnia{ 18440bbfda8aSnia twm_win->ewmhFlags = 0; 18450bbfda8aSnia 18460bbfda8aSnia Atom type = EwmhGetWindowProperty(twm_win->w, XA__NET_WM_WINDOW_TYPE, XA_ATOM); 18470bbfda8aSnia 18480bbfda8aSnia if(type == XA__NET_WM_WINDOW_TYPE_DESKTOP) { 18490bbfda8aSnia twm_win->ewmhWindowType = wt_Desktop; 18500bbfda8aSnia } 18510bbfda8aSnia else if(type == XA__NET_WM_WINDOW_TYPE_DOCK) { 18520bbfda8aSnia twm_win->ewmhWindowType = wt_Dock; 18530bbfda8aSnia } 18540bbfda8aSnia else { 18550bbfda8aSnia twm_win->ewmhWindowType = wt_Normal; 18560bbfda8aSnia } 18570bbfda8aSnia EwmhGetStrut(twm_win, false); 18580bbfda8aSnia /* Only the 3 listed states are supported for now */ 18590bbfda8aSnia twm_win->ewmhFlags |= EwmhGet_NET_WM_STATE(twm_win) & 18600bbfda8aSnia (EWMH_STATE_ABOVE | EWMH_STATE_BELOW | EWMH_STATE_SHADED); 18610bbfda8aSnia} 18620bbfda8aSnia 18630bbfda8aSnia/* Only used in initially mapping a window */ 18640bbfda8aSniaint EwmhGetInitPriority(TwmWindow *twm_win) 18650bbfda8aSnia{ 18660bbfda8aSnia switch(twm_win->ewmhWindowType) { 18670bbfda8aSnia case wt_Desktop: 18680bbfda8aSnia return EWMH_PRI_DESKTOP; 18690bbfda8aSnia case wt_Dock: 18700bbfda8aSnia return EWMH_PRI_DOCK; 18710bbfda8aSnia default: 18720bbfda8aSnia return 0; 18730bbfda8aSnia } 18740bbfda8aSnia} 18750bbfda8aSnia 18760bbfda8aSniabool EwmhHasBorder(TwmWindow *twm_win) 18770bbfda8aSnia{ 18780bbfda8aSnia switch(twm_win->ewmhWindowType) { 18790bbfda8aSnia case wt_Desktop: 18800bbfda8aSnia case wt_Dock: 18810bbfda8aSnia return false; 18820bbfda8aSnia default: 18830bbfda8aSnia return true; 18840bbfda8aSnia } 18850bbfda8aSnia} 18860bbfda8aSnia 18870bbfda8aSniabool EwmhHasTitle(TwmWindow *twm_win) 18880bbfda8aSnia{ 18890bbfda8aSnia switch(twm_win->ewmhWindowType) { 18900bbfda8aSnia case wt_Desktop: 18910bbfda8aSnia case wt_Dock: 18920bbfda8aSnia return false; 18930bbfda8aSnia default: 18940bbfda8aSnia return true; 18950bbfda8aSnia } 18960bbfda8aSnia} 18970bbfda8aSnia 18980bbfda8aSniabool EwmhOnWindowRing(TwmWindow *twm_win) 18990bbfda8aSnia{ 19000bbfda8aSnia switch(twm_win->ewmhWindowType) { 19010bbfda8aSnia case wt_Desktop: 19020bbfda8aSnia case wt_Dock: 19030bbfda8aSnia return false; 19040bbfda8aSnia default: 19050bbfda8aSnia return true; 19060bbfda8aSnia } 19070bbfda8aSnia} 19080bbfda8aSnia 19090bbfda8aSnia/* 19100bbfda8aSnia * Recalculate the effective border values from the remembered struts. 19110bbfda8aSnia * Interestingly it is not documented how to do that. 19120bbfda8aSnia * Usually only one dock is present on each side, so it shouldn't matter 19130bbfda8aSnia * too much, but I presume that maximizing the values is the thing to do. 19140bbfda8aSnia */ 19150bbfda8aSniastatic void EwmhRecalculateStrut(void) 19160bbfda8aSnia{ 19170bbfda8aSnia int left = 0; 19180bbfda8aSnia int right = 0; 19190bbfda8aSnia int top = 0; 19200bbfda8aSnia int bottom = 0; 19210bbfda8aSnia EwmhStrut *strut = Scr->ewmhStruts; 19220bbfda8aSnia 19230bbfda8aSnia while(strut != NULL) { 19240bbfda8aSnia left = max(left, strut->left); 19250bbfda8aSnia right = max(right, strut->right); 19260bbfda8aSnia top = max(top, strut->top); 19270bbfda8aSnia bottom = max(bottom, strut->bottom); 19280bbfda8aSnia 19290bbfda8aSnia strut = strut->next; 19300bbfda8aSnia } 19310bbfda8aSnia 19320bbfda8aSnia Scr->BorderLeft = left; 19330bbfda8aSnia Scr->BorderRight = right; 19340bbfda8aSnia Scr->BorderTop = top; 19350bbfda8aSnia Scr->BorderBottom = bottom; 19360bbfda8aSnia 1937b18c2d1eSnia // Bordered layout may have changed 1938b18c2d1eSnia Scr->BorderedLayout = RLayoutCopyCropped(Scr->Layout, 1939b18c2d1eSnia left, right, top, bottom); 1940b18c2d1eSnia if(Scr->BorderedLayout == NULL) { 1941b18c2d1eSnia Scr->BorderedLayout = Scr->Layout; // nothing to crop 1942b18c2d1eSnia } 1943b18c2d1eSnia 19440bbfda8aSnia EwmhSet_NET_WORKAREA(Scr); 19450bbfda8aSnia} 19460bbfda8aSnia/* 19470bbfda8aSnia * Check _NET_WM_STRUT_PARTIAL or _NET_WM_STRUT. 19480bbfda8aSnia * These are basically automatic settings for Border{Left,Right,Top,Bottom}. 19490bbfda8aSnia * 19500bbfda8aSnia * If any values are found, collect them in a list of strut values 19510bbfda8aSnia * belonging to Scr. When a window is added or removed that has struts, 19520bbfda8aSnia * the new effective value must be calculated. The expectation is that 19530bbfda8aSnia * at most a handful of windows will have struts. 19540bbfda8aSnia * 19550bbfda8aSnia * If update is true, this is called as an update for an existing window. 19560bbfda8aSnia */ 19570bbfda8aSniastatic void EwmhGetStrut(TwmWindow *twm_win, bool update) 19580bbfda8aSnia{ 19590bbfda8aSnia unsigned long nitems; 19600bbfda8aSnia unsigned long *prop; 19610bbfda8aSnia EwmhStrut *strut; 19620bbfda8aSnia 19630bbfda8aSnia prop = EwmhGetWindowProperties(twm_win->w, 19640bbfda8aSnia XA__NET_WM_STRUT_PARTIAL, XA_CARDINAL, 19650bbfda8aSnia &nitems); 19660bbfda8aSnia if(prop == NULL) { 19670bbfda8aSnia prop = EwmhGetWindowProperties(twm_win->w, 19680bbfda8aSnia XA__NET_WM_STRUT, XA_CARDINAL, &nitems); 19690bbfda8aSnia if(prop == NULL) { 19700bbfda8aSnia return; 19710bbfda8aSnia } 19720bbfda8aSnia } 19730bbfda8aSnia 19740bbfda8aSnia 19750bbfda8aSnia if(nitems < 4) { 19760bbfda8aSnia#ifdef DEBUG_EWMH 19770bbfda8aSnia /* This happens a lot, despite returning Success ??? */ 19780bbfda8aSnia printf("struts: prop = %p, nitems = %ld\n", prop, nitems); 19790bbfda8aSnia#endif 19800bbfda8aSnia XFree(prop); 19810bbfda8aSnia return; 19820bbfda8aSnia } 19830bbfda8aSnia#ifdef DEBUG_EWMH 19840bbfda8aSnia printf("struts: left %ld, right %ld, top %ld, bottom %ld\n", 19850bbfda8aSnia prop[0], prop[1], prop[2], prop[3]); 19860bbfda8aSnia#endif 19870bbfda8aSnia 19880bbfda8aSnia /* 19890bbfda8aSnia * If there were no struts before, the user configured margins are set 19900bbfda8aSnia * in Border{Left,Right,Top,Bottom}. In order not to lose those values 19910bbfda8aSnia * when recalculating them, convert them to struts for a dummy window. 19920bbfda8aSnia */ 19930bbfda8aSnia 19940bbfda8aSnia if(Scr->ewmhStruts == NULL && 19950bbfda8aSnia (Scr->BorderLeft | 19960bbfda8aSnia Scr->BorderRight | 19970bbfda8aSnia Scr->BorderTop | 19980bbfda8aSnia Scr->BorderBottom) != 0) { 19990bbfda8aSnia strut = calloc(1, sizeof(EwmhStrut)); 20000bbfda8aSnia if(strut == NULL) { 20010bbfda8aSnia XFree(prop); 20020bbfda8aSnia return; 20030bbfda8aSnia } 20040bbfda8aSnia 20050bbfda8aSnia strut->next = NULL; 20060bbfda8aSnia strut->win = NULL; 20070bbfda8aSnia strut->left = Scr->BorderLeft; 20080bbfda8aSnia strut->right = Scr->BorderRight; 20090bbfda8aSnia strut->top = Scr->BorderTop; 20100bbfda8aSnia strut->bottom = Scr->BorderBottom; 20110bbfda8aSnia 20120bbfda8aSnia Scr->ewmhStruts = strut; 20130bbfda8aSnia } 20140bbfda8aSnia 20150bbfda8aSnia strut = NULL; 20160bbfda8aSnia 20170bbfda8aSnia /* 20180bbfda8aSnia * Find the struts of the window that we're supposed to be updating. 20190bbfda8aSnia * If not found, there is no problem: we'll just allocate a new 20200bbfda8aSnia * record. 20210bbfda8aSnia */ 20220bbfda8aSnia 20230bbfda8aSnia if(update) { 20240bbfda8aSnia strut = Scr->ewmhStruts; 20250bbfda8aSnia 20260bbfda8aSnia while(strut != NULL) { 20270bbfda8aSnia if(strut->win == twm_win) { 20280bbfda8aSnia break; 20290bbfda8aSnia } 20300bbfda8aSnia strut = strut->next; 20310bbfda8aSnia } 20320bbfda8aSnia } 20330bbfda8aSnia 20340bbfda8aSnia /* 20350bbfda8aSnia * If needed, allocate a new struts record and link it in. 20360bbfda8aSnia */ 20370bbfda8aSnia if(strut == NULL) { 20380bbfda8aSnia strut = calloc(1, sizeof(EwmhStrut)); 20390bbfda8aSnia if(strut == NULL) { 20400bbfda8aSnia XFree(prop); 20410bbfda8aSnia return; 20420bbfda8aSnia } 20430bbfda8aSnia 20440bbfda8aSnia strut->next = Scr->ewmhStruts; 20450bbfda8aSnia Scr->ewmhStruts = strut; 20460bbfda8aSnia } 20470bbfda8aSnia 20480bbfda8aSnia strut->win = twm_win; 20490bbfda8aSnia strut->left = prop[0]; 20500bbfda8aSnia strut->right = prop[1]; 20510bbfda8aSnia strut->top = prop[2]; 20520bbfda8aSnia strut->bottom = prop[3]; 20530bbfda8aSnia 20540bbfda8aSnia XFree(prop); 20550bbfda8aSnia 20560bbfda8aSnia /* 20570bbfda8aSnia * Mark this window as having contributed some struts. 20580bbfda8aSnia * This can be checked and undone when the window is deleted. 20590bbfda8aSnia */ 20600bbfda8aSnia twm_win->ewmhFlags |= EWMH_HAS_STRUT; 20610bbfda8aSnia 20620bbfda8aSnia EwmhRecalculateStrut(); 20630bbfda8aSnia} 20640bbfda8aSnia 20650bbfda8aSnia/* 20660bbfda8aSnia * Remove the struts associated with the given window from the 20670bbfda8aSnia * remembered list. If found, recalculate the effective borders. 20680bbfda8aSnia */ 20690bbfda8aSniastatic void EwmhRemoveStrut(TwmWindow *twm_win) 20700bbfda8aSnia{ 20710bbfda8aSnia EwmhStrut **prev = &Scr->ewmhStruts; 20720bbfda8aSnia EwmhStrut *strut = Scr->ewmhStruts; 20730bbfda8aSnia 20740bbfda8aSnia while(strut != NULL) { 20750bbfda8aSnia if(strut->win == twm_win) { 20760bbfda8aSnia twm_win->ewmhFlags &= ~EWMH_HAS_STRUT; 20770bbfda8aSnia 20780bbfda8aSnia *prev = strut->next; 20790bbfda8aSnia free(strut); 20800bbfda8aSnia 20810bbfda8aSnia EwmhRecalculateStrut(); 20820bbfda8aSnia 20830bbfda8aSnia break; 20840bbfda8aSnia } 20850bbfda8aSnia prev = &strut->next; 20860bbfda8aSnia strut = strut->next; 20870bbfda8aSnia } 20880bbfda8aSnia} 20890bbfda8aSnia 2090b18c2d1eSnia 2091b18c2d1eSnia/** 2092b18c2d1eSnia * Set _NET_FRAME_EXTENTS property. 2093b18c2d1eSnia * This tells the client how much space is being taken up by the window 2094b18c2d1eSnia * decorations. Some clients may need this information to position other 2095b18c2d1eSnia * windows on top of themselves. e.g., Firefox's form autofill and 2096b18c2d1eSnia * context menu will be positioned a bit wrong (high, by the height of 2097b18c2d1eSnia * the titlebar) without this. 2098b18c2d1eSnia */ 2099b18c2d1eSniavoid EwmhSet_NET_FRAME_EXTENTS(TwmWindow *twm_win) 2100b18c2d1eSnia{ 2101b18c2d1eSnia long data[4]; 2102b18c2d1eSnia const long w = twm_win->frame_bw3D + twm_win->frame_bw; 2103b18c2d1eSnia 2104b18c2d1eSnia data[0] = w; // left 2105b18c2d1eSnia data[1] = w; // right 2106b18c2d1eSnia data[2] = twm_win->title_height + w; // top 2107b18c2d1eSnia data[3] = w; // bottom 2108b18c2d1eSnia 2109b18c2d1eSnia XChangeProperty(dpy, twm_win->w, 2110b18c2d1eSnia XA__NET_FRAME_EXTENTS, XA_CARDINAL, 2111b18c2d1eSnia 32, PropModeReplace, 2112b18c2d1eSnia (unsigned char *)data, 4); 2113b18c2d1eSnia} 2114b18c2d1eSnia 2115b18c2d1eSnia 21160bbfda8aSniavoid EwmhSet_NET_SHOWING_DESKTOP(int state) 21170bbfda8aSnia{ 21180bbfda8aSnia unsigned long prop[1]; 21190bbfda8aSnia 21200bbfda8aSnia prop[0] = state; 21210bbfda8aSnia 21220bbfda8aSnia XChangeProperty(dpy, Scr->XineramaRoot, XA__NET_SHOWING_DESKTOP, XA_CARDINAL, 21230bbfda8aSnia 32, 21240bbfda8aSnia PropModeReplace, (unsigned char *)prop, 1); 21250bbfda8aSnia} 21260bbfda8aSnia 21270bbfda8aSnia/* 21280bbfda8aSnia * Set _NET_WM_STATE. 21290bbfda8aSnia * 21300bbfda8aSnia * TwmWindow.ewmhFlags keeps track of the atoms that should be in 21310bbfda8aSnia * the list, so that we don't have to fetch or recalculate them all. 21320bbfda8aSnia * 21330bbfda8aSnia * XXX It's not clear that the theoretical performance gain and edge-case 21340bbfda8aSnia * bug avoidance of the 'changes' arg is worth the complexity and 21350bbfda8aSnia * edge-case bug creation it brings. Consider removing it. 21360bbfda8aSnia */ 21370bbfda8aSniavoid EwmhSet_NET_WM_STATE(TwmWindow *twm_win, int changes) 21380bbfda8aSnia{ 21390bbfda8aSnia unsigned long prop[10]; 21400bbfda8aSnia int flags; 21410bbfda8aSnia int i; 21420bbfda8aSnia 21430bbfda8aSnia if(changes & EWMH_STATE_MAXIMIZED_VERT) { 21440bbfda8aSnia int newFlags = 0; 21450bbfda8aSnia 21460bbfda8aSnia switch(twm_win->zoomed) { 21470bbfda8aSnia case F_FULLZOOM: 21480bbfda8aSnia newFlags = EWMH_STATE_MAXIMIZED_VERT | 21490bbfda8aSnia EWMH_STATE_MAXIMIZED_HORZ; 21500bbfda8aSnia break; 21510bbfda8aSnia case F_ZOOM: 21520bbfda8aSnia case F_LEFTZOOM: 21530bbfda8aSnia case F_RIGHTZOOM: 21540bbfda8aSnia newFlags = EWMH_STATE_MAXIMIZED_VERT; 21550bbfda8aSnia break; 21560bbfda8aSnia case F_HORIZOOM: 21570bbfda8aSnia case F_TOPZOOM: 21580bbfda8aSnia case F_BOTTOMZOOM: 21590bbfda8aSnia newFlags = EWMH_STATE_MAXIMIZED_HORZ; 21600bbfda8aSnia break; 21610bbfda8aSnia case F_FULLSCREENZOOM: 21620bbfda8aSnia newFlags = EWMH_STATE_FULLSCREEN; 21630bbfda8aSnia break; 21640bbfda8aSnia } 21650bbfda8aSnia 21660bbfda8aSnia twm_win->ewmhFlags &= ~(EWMH_STATE_MAXIMIZED_VERT | 21670bbfda8aSnia EWMH_STATE_MAXIMIZED_HORZ | 21680bbfda8aSnia EWMH_STATE_FULLSCREEN); 21690bbfda8aSnia twm_win->ewmhFlags |= newFlags; 21700bbfda8aSnia } 21710bbfda8aSnia 21720bbfda8aSnia if(changes & EWMH_STATE_SHADED) { 21730bbfda8aSnia if(twm_win->squeezed) { 21740bbfda8aSnia twm_win->ewmhFlags |= EWMH_STATE_SHADED; 21750bbfda8aSnia } 21760bbfda8aSnia else { 21770bbfda8aSnia twm_win->ewmhFlags &= ~EWMH_STATE_SHADED; 21780bbfda8aSnia } 21790bbfda8aSnia } 21800bbfda8aSnia 21810bbfda8aSnia if(changes & (EWMH_STATE_ABOVE | EWMH_STATE_BELOW)) { 21820bbfda8aSnia int pri = OtpEffectiveDisplayPriority(twm_win); 21830bbfda8aSnia 21840bbfda8aSnia twm_win->ewmhFlags &= ~(EWMH_STATE_ABOVE | EWMH_STATE_BELOW); 21850bbfda8aSnia 21860bbfda8aSnia /* 21870bbfda8aSnia * If it's a DOCK or DESKTOP, and where we expect those to be, we 21880bbfda8aSnia * consider that there's nothing to tell it. Otherwise, we tell 21890bbfda8aSnia * it ABOVE/BELOW based on where it effectively is. 21900bbfda8aSnia */ 21910bbfda8aSnia if(twm_win->ewmhWindowType == wt_Dock && pri == EWMH_PRI_DOCK) { 21920bbfda8aSnia pri = 0; 21930bbfda8aSnia } 21940bbfda8aSnia if(twm_win->ewmhWindowType == wt_Desktop && pri == EWMH_PRI_DESKTOP) { 21950bbfda8aSnia pri = 0; 21960bbfda8aSnia } 21970bbfda8aSnia 21980bbfda8aSnia if(pri > 0) { 21990bbfda8aSnia twm_win->ewmhFlags |= EWMH_STATE_ABOVE; 22000bbfda8aSnia } 22010bbfda8aSnia else if(pri < 0) { 22020bbfda8aSnia twm_win->ewmhFlags |= EWMH_STATE_BELOW; 22030bbfda8aSnia } 22040bbfda8aSnia } 22050bbfda8aSnia 22060bbfda8aSnia flags = twm_win->ewmhFlags; 22070bbfda8aSnia i = 0; 22080bbfda8aSnia 22090bbfda8aSnia if(flags & EWMH_STATE_MAXIMIZED_VERT) { 22100bbfda8aSnia prop[i++] = XA__NET_WM_STATE_MAXIMIZED_VERT; 22110bbfda8aSnia } 22120bbfda8aSnia if(flags & EWMH_STATE_MAXIMIZED_HORZ) { 22130bbfda8aSnia prop[i++] = XA__NET_WM_STATE_MAXIMIZED_HORZ; 22140bbfda8aSnia } 22150bbfda8aSnia if(flags & EWMH_STATE_FULLSCREEN) { 22160bbfda8aSnia prop[i++] = XA__NET_WM_STATE_FULLSCREEN; 22170bbfda8aSnia } 22180bbfda8aSnia if(flags & EWMH_STATE_SHADED) { 22190bbfda8aSnia prop[i++] = XA__NET_WM_STATE_SHADED; 22200bbfda8aSnia } 22210bbfda8aSnia if(flags & EWMH_STATE_ABOVE) { 22220bbfda8aSnia prop[i++] = XA__NET_WM_STATE_ABOVE; 22230bbfda8aSnia } 22240bbfda8aSnia if(flags & EWMH_STATE_BELOW) { 22250bbfda8aSnia prop[i++] = XA__NET_WM_STATE_BELOW; 22260bbfda8aSnia } 22270bbfda8aSnia 22280bbfda8aSnia XChangeProperty(dpy, twm_win->w, XA__NET_WM_STATE, XA_ATOM, 32, 22290bbfda8aSnia PropModeReplace, (unsigned char *)prop, i); 22300bbfda8aSnia} 22310bbfda8aSnia 22320bbfda8aSnia/* 22330bbfda8aSnia * Get the initial state of _NET_WM_STATE. 22340bbfda8aSnia * 22350bbfda8aSnia * Only some of the flags are supported when initially creating a window. 22360bbfda8aSnia */ 22370bbfda8aSniastatic int EwmhGet_NET_WM_STATE(TwmWindow *twm_win) 22380bbfda8aSnia{ 22390bbfda8aSnia int flags = 0; 22400bbfda8aSnia unsigned long *prop; 22410bbfda8aSnia unsigned long nitems; 22420bbfda8aSnia int i; 22430bbfda8aSnia 22440bbfda8aSnia prop = EwmhGetWindowProperties(twm_win->w, XA__NET_WM_STATE, XA_ATOM, &nitems); 22450bbfda8aSnia 22460bbfda8aSnia if(prop) { 22470bbfda8aSnia for(i = 0; i < nitems; i++) { 22480bbfda8aSnia flags |= atomToFlag(prop[i]); 22490bbfda8aSnia } 22500bbfda8aSnia 22510bbfda8aSnia XFree(prop); 22520bbfda8aSnia } 22530bbfda8aSnia 22540bbfda8aSnia return flags; 22550bbfda8aSnia} 22560bbfda8aSnia 22570bbfda8aSnia/* 22580bbfda8aSnia * Set _NET_WORKAREA. 22590bbfda8aSnia */ 22600bbfda8aSniastatic void EwmhSet_NET_WORKAREA(ScreenInfo *scr) 22610bbfda8aSnia{ 22620bbfda8aSnia unsigned long prop[4]; 22630bbfda8aSnia 22640bbfda8aSnia /* x */ prop[0] = scr->BorderLeft; 22650bbfda8aSnia /* y */ prop[1] = scr->BorderTop; 22660bbfda8aSnia /* w */ prop[2] = scr->rootw - scr->BorderLeft - scr->BorderRight; 22670bbfda8aSnia /* h */ prop[3] = scr->rooth - scr->BorderTop - scr->BorderBottom; 22680bbfda8aSnia 22690bbfda8aSnia XChangeProperty(dpy, Scr->XineramaRoot, XA__NET_WORKAREA, XA_CARDINAL, 32, 22700bbfda8aSnia PropModeReplace, (unsigned char *)prop, 4); 22710bbfda8aSnia} 2272