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