1 #include "config.h"
2 #include "yxapp.h"
3 #include "yfull.h"
4 #include "ymenu.h"
5 #include "wmmgr.h"
6 #include "MwmUtil.h"
7 #include "ypointer.h"
8 #include "yxcontext.h"
9 #include "guievent.h"
10 #include "intl.h"
11 #undef override
12 #include <X11/Xproto.h>
13 #ifdef XINERAMA
14 #include <X11/extensions/Xinerama.h>
15 #endif
16 #include <X11/extensions/Xcomposite.h>
17 #include <X11/extensions/XShm.h>
18 
19 YXApplication *xapp = nullptr;
20 
21 YDesktop *desktop = nullptr;
22 YContext<YWindow> windowContext;
23 
24 bool YXApplication::synchronizeX11;
25 bool YXApplication::alphaBlending;
26 Window YXApplication::ignorable;
27 
28 Atom _XA_WM_CHANGE_STATE;
29 Atom _XA_WM_CLASS;
30 Atom _XA_WM_CLIENT_LEADER;
31 Atom _XA_WM_CLIENT_MACHINE;
32 Atom _XA_WM_COLORMAP_NOTIFY;
33 Atom _XA_WM_COLORMAP_WINDOWS;
34 Atom _XA_WM_COMMAND;
35 Atom _XA_WM_DELETE_WINDOW;
36 Atom _XA_WM_DESKTOP;
37 Atom _XA_WM_HINTS;
38 Atom _XA_WM_ICON_NAME;
39 Atom _XA_WM_ICON_SIZE;
40 Atom _XA_WM_LOCALE_NAME;
41 Atom _XA_WM_NAME;
42 Atom _XA_WM_NORMAL_HINTS;
43 Atom _XA_WM_PROTOCOLS;
44 Atom _XA_WM_SIZE_HINTS;
45 Atom _XA_WM_STATE;
46 Atom _XA_WM_TAKE_FOCUS;
47 Atom _XA_WM_TRANSIENT_FOR;
48 Atom _XA_WM_WINDOW_ROLE;
49 Atom _XA_WM_ZOOM_HINTS;
50 
51 Atom _XATOM_MWM_HINTS;
52 Atom _XATOM_MWM_INFO;
53 Atom _XA_WINDOW_ROLE;
54 Atom _XA_SM_CLIENT_ID;
55 Atom _XA_ICEWM_ACTION;
56 Atom _XA_ICEWM_GUIEVENT;
57 Atom _XA_ICEWM_HINT;
58 Atom _XA_ICEWM_FONT_PATH;
59 Atom _XA_ICEWMBG_IMAGE;
60 Atom _XA_XROOTPMAP_ID;
61 Atom _XA_XROOTCOLOR_PIXEL;
62 Atom _XA_GDK_TIMESTAMP_PROP;
63 Atom _XA_CLIPBOARD;
64 Atom _XA_MANAGER;
65 Atom _XA_TARGETS;
66 Atom _XA_XEMBED;
67 Atom _XA_XEMBED_INFO;
68 Atom _XA_UTF8_STRING;
69 
70 Atom _XA_WIN_ICONS;
71 Atom _XA_WIN_LAYER;
72 Atom _XA_WIN_PROTOCOLS;
73 Atom _XA_WIN_TRAY;
74 
75 Atom _XA_NET_ACTIVE_WINDOW;
76 Atom _XA_NET_CLIENT_LIST;
77 Atom _XA_NET_CLIENT_LIST_STACKING;
78 Atom _XA_NET_CLOSE_WINDOW;
79 Atom _XA_NET_CURRENT_DESKTOP;
80 Atom _XA_NET_DESKTOP_GEOMETRY;
81 Atom _XA_NET_DESKTOP_LAYOUT;
82 Atom _XA_NET_DESKTOP_NAMES;
83 Atom _XA_NET_DESKTOP_VIEWPORT;
84 Atom _XA_NET_FRAME_EXTENTS;
85 Atom _XA_NET_MOVERESIZE_WINDOW;
86 Atom _XA_NET_NUMBER_OF_DESKTOPS;
87 Atom _XA_NET_PROPERTIES;
88 Atom _XA_NET_REQUEST_FRAME_EXTENTS;
89 Atom _XA_NET_RESTACK_WINDOW;
90 Atom _XA_NET_SHOWING_DESKTOP;
91 Atom _XA_NET_STARTUP_ID;
92 Atom _XA_NET_STARTUP_INFO_BEGIN;
93 Atom _XA_NET_STARTUP_INFO;
94 Atom _XA_NET_SUPPORTED;
95 Atom _XA_NET_SUPPORTING_WM_CHECK;
96 Atom _XA_NET_SYSTEM_TRAY_MESSAGE_DATA;
97 Atom _XA_NET_SYSTEM_TRAY_OPCODE;
98 Atom _XA_NET_SYSTEM_TRAY_ORIENTATION;
99 Atom _XA_NET_SYSTEM_TRAY_VISUAL;
100 Atom _XA_NET_VIRTUAL_ROOTS;
101 Atom _XA_NET_WM_ACTION_ABOVE;
102 Atom _XA_NET_WM_ACTION_BELOW;
103 Atom _XA_NET_WM_ACTION_CHANGE_DESKTOP;
104 Atom _XA_NET_WM_ACTION_CLOSE;
105 Atom _XA_NET_WM_ACTION_FULLSCREEN;
106 Atom _XA_NET_WM_ACTION_MAXIMIZE_HORZ;
107 Atom _XA_NET_WM_ACTION_MAXIMIZE_VERT;
108 Atom _XA_NET_WM_ACTION_MINIMIZE;
109 Atom _XA_NET_WM_ACTION_MOVE;
110 Atom _XA_NET_WM_ACTION_RESIZE;
111 Atom _XA_NET_WM_ACTION_SHADE;
112 Atom _XA_NET_WM_ACTION_STICK;
113 Atom _XA_NET_WM_ALLOWED_ACTIONS;
114 Atom _XA_NET_WM_BYPASS_COMPOSITOR;
115 Atom _XA_NET_WM_DESKTOP;
116 Atom _XA_NET_WM_FULL_PLACEMENT;
117 Atom _XA_NET_WM_FULLSCREEN_MONITORS;
118 Atom _XA_NET_WM_HANDLED_ICONS;
119 Atom _XA_NET_WM_ICON_GEOMETRY;
120 Atom _XA_NET_WM_ICON_NAME;
121 Atom _XA_NET_WM_ICON;
122 Atom _XA_NET_WM_MOVERESIZE;
123 Atom _XA_NET_WM_NAME;
124 Atom _XA_NET_WM_OPAQUE_REGION;
125 Atom _XA_NET_WM_PID;
126 Atom _XA_NET_WM_PING;
127 Atom _XA_NET_WM_STATE;
128 Atom _XA_NET_WM_STATE_ABOVE;
129 Atom _XA_NET_WM_STATE_BELOW;
130 Atom _XA_NET_WM_STATE_DEMANDS_ATTENTION;
131 Atom _XA_NET_WM_STATE_FOCUSED;
132 Atom _XA_NET_WM_STATE_FULLSCREEN;
133 Atom _XA_NET_WM_STATE_HIDDEN;
134 Atom _XA_NET_WM_STATE_MAXIMIZED_HORZ;
135 Atom _XA_NET_WM_STATE_MAXIMIZED_VERT;
136 Atom _XA_NET_WM_STATE_MODAL;
137 Atom _XA_NET_WM_STATE_SHADED;
138 Atom _XA_NET_WM_STATE_SKIP_PAGER;
139 Atom _XA_NET_WM_STATE_SKIP_TASKBAR;
140 Atom _XA_NET_WM_STATE_STICKY;
141 Atom _XA_NET_WM_STRUT;
142 Atom _XA_NET_WM_STRUT_PARTIAL;
143 Atom _XA_NET_WM_SYNC_REQUEST;
144 Atom _XA_NET_WM_SYNC_REQUEST_COUNTER;
145 Atom _XA_NET_WM_USER_TIME;
146 Atom _XA_NET_WM_USER_TIME_WINDOW;
147 Atom _XA_NET_WM_VISIBLE_ICON_NAME;
148 Atom _XA_NET_WM_VISIBLE_NAME;
149 Atom _XA_NET_WM_WINDOW_OPACITY;
150 Atom _XA_NET_WM_WINDOW_TYPE;
151 Atom _XA_NET_WM_WINDOW_TYPE_COMBO;
152 Atom _XA_NET_WM_WINDOW_TYPE_DESKTOP;
153 Atom _XA_NET_WM_WINDOW_TYPE_DIALOG;
154 Atom _XA_NET_WM_WINDOW_TYPE_DND;
155 Atom _XA_NET_WM_WINDOW_TYPE_DOCK;
156 Atom _XA_NET_WM_WINDOW_TYPE_DROPDOWN_MENU;
157 Atom _XA_NET_WM_WINDOW_TYPE_MENU;
158 Atom _XA_NET_WM_WINDOW_TYPE_NORMAL;
159 Atom _XA_NET_WM_WINDOW_TYPE_NOTIFICATION;
160 Atom _XA_NET_WM_WINDOW_TYPE_POPUP_MENU;
161 Atom _XA_NET_WM_WINDOW_TYPE_SPLASH;
162 Atom _XA_NET_WM_WINDOW_TYPE_TOOLBAR;
163 Atom _XA_NET_WM_WINDOW_TYPE_TOOLTIP;
164 Atom _XA_NET_WM_WINDOW_TYPE_UTILITY;
165 Atom _XA_NET_WORKAREA;
166 
167 Atom _XA_KWM_DOCKWINDOW;
168 Atom _XA_KWM_WIN_ICON;
169 
170 Atom _XA_KDE_NET_SYSTEM_TRAY_WINDOWS;
171 Atom _XA_KDE_NET_WM_FRAME_STRUT;
172 Atom _XA_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR;
173 Atom _XA_KDE_NET_WM_WINDOW_TYPE_OVERRIDE;
174 Atom _XA_KDE_SPLASH_PROGRESS;
175 Atom _XA_KDE_WM_CHANGE_STATE;
176 
177 Atom XA_XdndAware;
178 Atom XA_XdndDrop;
179 Atom XA_XdndEnter;
180 Atom XA_XdndFinished;
181 Atom XA_XdndLeave;
182 Atom XA_XdndPosition;
183 Atom XA_XdndProxy;
184 Atom XA_XdndStatus;
185 Atom XA_XdndActionCopy;
186 Atom XA_XdndActionMove;
187 Atom XA_XdndActionLink;
188 Atom XA_XdndActionAsk;
189 Atom XA_XdndActionPrivate;
190 
191 YExtension composite;
192 YExtension damage;
193 YExtension fixes;
194 YExtension render;
195 YExtension shapes;
196 YExtension xrandr;
197 YExtension xinerama;
198 YExtension xshm;
199 
200 #ifdef DEBUG
201 int xeventcount = 0;
202 #endif
203 
204 class YClipboard: public YWindow {
205 public:
setData(mstring data)206     void setData(mstring data) {
207         fData = data;
208         if (length() == 0)
209             clearSelection(false);
210         else
211             acquireSelection(false);
212     }
handleSelectionClear(const XSelectionClearEvent & clear)213     void handleSelectionClear(const XSelectionClearEvent &clear) {
214         if (clear.selection == _XA_CLIPBOARD) {
215             fData = null;
216         }
217     }
handleSelectionRequest(const XSelectionRequestEvent & request)218     void handleSelectionRequest(const XSelectionRequestEvent &request) {
219         if (request.selection == _XA_CLIPBOARD) {
220             XSelectionEvent notify;
221 
222             notify.type = SelectionNotify;
223             notify.requestor = request.requestor;
224             notify.selection = request.selection;
225             notify.target = request.target;
226             notify.time = request.time;
227             notify.property = request.property;
228 
229             if (request.selection == _XA_CLIPBOARD &&
230                 (request.target == XA_STRING ||
231                  request.target == _XA_UTF8_STRING) &&
232                 length() > 0)
233             {
234                 unsigned char *data =
235                     reinterpret_cast<unsigned char *>(
236                             const_cast<char *>(fData.c_str()));
237                 XChangeProperty(xapp->display(),
238                                 request.requestor,
239                                 request.property,
240                                 request.target,
241                                 8, PropModeReplace,
242                                 data, length());
243             } else if (request.selection == _XA_CLIPBOARD &&
244                        request.target == _XA_TARGETS &&
245                        length() > 0)
246             {
247                 Atom targets[] = {
248                     XA_STRING,
249                     _XA_UTF8_STRING,
250                 };
251                 unsigned char* data =
252                     reinterpret_cast<unsigned char *>(targets);
253                 const int count = int ACOUNT(targets);
254 
255                 XChangeProperty(xapp->display(),
256                                 request.requestor,
257                                 request.property,
258                                 request.target,
259                                 32, PropModeReplace,
260                                 data, count);
261             } else {
262                 notify.property = None;
263             }
264 
265             XSendEvent(xapp->display(), notify.requestor, False, None,
266                        reinterpret_cast<XEvent *>(&notify));
267         }
268     }
269 
length() const270     int length() const {
271         return fData.length();
272     }
273 
274 private:
275     mstring fData;
276 };
277 
278 YAtomName YXApplication::atom_info[] = {
279     { &_XA_WM_CHANGE_STATE                  , "WM_CHANGE_STATE" },
280     { &_XA_WM_CLASS                         , "WM_CLASS" },
281     { &_XA_WM_CLIENT_LEADER                 , "WM_CLIENT_LEADER" },
282     { &_XA_WM_CLIENT_MACHINE                , "WM_CLIENT_MACHINE" },
283     { &_XA_WM_COLORMAP_NOTIFY               , "WM_COLORMAP_NOTIFY" },
284     { &_XA_WM_COLORMAP_WINDOWS              , "WM_COLORMAP_WINDOWS" },
285     { &_XA_WM_COMMAND                       , "WM_COMMAND" },
286     { &_XA_WM_DELETE_WINDOW                 , "WM_DELETE_WINDOW" },
287     { &_XA_WM_DESKTOP                       , "WM_DESKTOP" },
288     { &_XA_WM_HINTS                         , "WM_HINTS" },
289     { &_XA_WM_ICON_NAME                     , "WM_ICON_NAME" },
290     { &_XA_WM_ICON_SIZE                     , "WM_ICON_SIZE" },
291     { &_XA_WM_LOCALE_NAME                   , "WM_LOCALE_NAME" },
292     { &_XA_WM_NAME                          , "WM_NAME" },
293     { &_XA_WM_NORMAL_HINTS                  , "WM_NORMAL_HINTS" },
294     { &_XA_WM_PROTOCOLS                     , "WM_PROTOCOLS" },
295     { &_XA_WM_SIZE_HINTS                    , "WM_SIZE_HINTS" },
296     { &_XA_WM_STATE                         , "WM_STATE" },
297     { &_XA_WM_TAKE_FOCUS                    , "WM_TAKE_FOCUS" },
298     { &_XA_WM_TRANSIENT_FOR                 , "WM_TRANSIENT_FOR" },
299     { &_XA_WM_WINDOW_ROLE                   , "WM_WINDOW_ROLE" },
300     { &_XA_WM_ZOOM_HINTS                    , "WM_ZOOM_HINTS" },
301 
302     { &_XA_WINDOW_ROLE                      , "WINDOW_ROLE" },
303     { &_XA_SM_CLIENT_ID                     , "SM_CLIENT_ID" },
304     { &_XA_ICEWM_ACTION                     , "_ICEWM_ACTION" },
305     { &_XA_ICEWM_GUIEVENT                   , XA_GUI_EVENT_NAME },
306     { &_XA_ICEWM_HINT                       , "_ICEWM_WINOPTHINT" },
307     { &_XA_ICEWM_FONT_PATH                  , "ICEWM_FONT_PATH" },
308     { &_XA_ICEWMBG_IMAGE                    , "_ICEWMBG_IMAGE" },
309     { &_XA_XROOTPMAP_ID                     , "_XROOTPMAP_ID" },
310     { &_XA_XROOTCOLOR_PIXEL                 , "_XROOTCOLOR_PIXEL" },
311     { &_XA_GDK_TIMESTAMP_PROP               , "GDK_TIMESTAMP_PROP" },
312     { &_XATOM_MWM_HINTS                     , _XA_MOTIF_WM_HINTS },
313     { &_XATOM_MWM_INFO                      , _XA_MOTIF_WM_INFO },
314 
315     { &_XA_KWM_DOCKWINDOW                   , "KWM_DOCKWINDOW" },
316     { &_XA_KWM_WIN_ICON                     , "KWM_WIN_ICON" },
317 
318     { &_XA_KDE_NET_SYSTEM_TRAY_WINDOWS      , "_KDE_NET_SYSTEM_TRAY_WINDOWS" },
319     { &_XA_KDE_NET_WM_FRAME_STRUT           , "_KDE_NET_WM_FRAME_STRUT" },
320     { &_XA_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR" },
321     { &_XA_KDE_NET_WM_WINDOW_TYPE_OVERRIDE  , "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE" },
322     { &_XA_KDE_SPLASH_PROGRESS              , "_KDE_SPLASH_PROGRESS" },
323     { &_XA_KDE_WM_CHANGE_STATE              , "_KDE_WM_CHANGE_STATE" },
324 
325     { &_XA_WIN_ICONS                        , XA_WIN_ICONS },
326     { &_XA_WIN_LAYER                        , XA_WIN_LAYER },
327     { &_XA_WIN_PROTOCOLS                    , XA_WIN_PROTOCOLS },
328     { &_XA_WIN_TRAY                         , XA_WIN_TRAY },
329 
330     { &_XA_NET_ACTIVE_WINDOW                , "_NET_ACTIVE_WINDOW" },
331     { &_XA_NET_CLIENT_LIST                  , "_NET_CLIENT_LIST" },
332     { &_XA_NET_CLIENT_LIST_STACKING         , "_NET_CLIENT_LIST_STACKING" },
333     { &_XA_NET_CLOSE_WINDOW                 , "_NET_CLOSE_WINDOW" },
334     { &_XA_NET_CURRENT_DESKTOP              , "_NET_CURRENT_DESKTOP" },
335     { &_XA_NET_DESKTOP_GEOMETRY             , "_NET_DESKTOP_GEOMETRY" },
336     { &_XA_NET_DESKTOP_LAYOUT               , "_NET_DESKTOP_LAYOUT" },
337     { &_XA_NET_DESKTOP_NAMES                , "_NET_DESKTOP_NAMES" },
338     { &_XA_NET_DESKTOP_VIEWPORT             , "_NET_DESKTOP_VIEWPORT" },
339     { &_XA_NET_FRAME_EXTENTS                , "_NET_FRAME_EXTENTS" },
340     { &_XA_NET_MOVERESIZE_WINDOW            , "_NET_MOVERESIZE_WINDOW" },
341     { &_XA_NET_NUMBER_OF_DESKTOPS           , "_NET_NUMBER_OF_DESKTOPS" },
342     { &_XA_NET_PROPERTIES                   , "_NET_PROPERTIES" },
343     { &_XA_NET_REQUEST_FRAME_EXTENTS        , "_NET_REQUEST_FRAME_EXTENTS" },
344     { &_XA_NET_RESTACK_WINDOW               , "_NET_RESTACK_WINDOW" },
345     { &_XA_NET_SHOWING_DESKTOP              , "_NET_SHOWING_DESKTOP" },
346     { &_XA_NET_STARTUP_ID                   , "_NET_STARTUP_ID" },
347     { &_XA_NET_STARTUP_INFO                 , "_NET_STARTUP_INFO" },
348     { &_XA_NET_STARTUP_INFO_BEGIN           , "_NET_STARTUP_INFO_BEGIN" },
349     { &_XA_NET_SUPPORTED                    , "_NET_SUPPORTED" },
350     { &_XA_NET_SUPPORTING_WM_CHECK          , "_NET_SUPPORTING_WM_CHECK" },
351     { &_XA_NET_SYSTEM_TRAY_MESSAGE_DATA     , "_NET_SYSTEM_TRAY_MESSAGE_DATA" },
352     { &_XA_NET_SYSTEM_TRAY_OPCODE           , "_NET_SYSTEM_TRAY_OPCODE" },
353     { &_XA_NET_SYSTEM_TRAY_ORIENTATION      , "_NET_SYSTEM_TRAY_ORIENTATION" },
354     { &_XA_NET_SYSTEM_TRAY_VISUAL           , "_NET_SYSTEM_TRAY_VISUAL" },
355     { &_XA_NET_VIRTUAL_ROOTS                , "_NET_VIRTUAL_ROOTS" },
356     { &_XA_NET_WM_ACTION_ABOVE              , "_NET_WM_ACTION_ABOVE" },
357     { &_XA_NET_WM_ACTION_BELOW              , "_NET_WM_ACTION_BELOW" },
358     { &_XA_NET_WM_ACTION_CHANGE_DESKTOP     , "_NET_WM_ACTION_CHANGE_DESKTOP" },
359     { &_XA_NET_WM_ACTION_CLOSE              , "_NET_WM_ACTION_CLOSE" },
360     { &_XA_NET_WM_ACTION_FULLSCREEN         , "_NET_WM_ACTION_FULLSCREEN" },
361     { &_XA_NET_WM_ACTION_MAXIMIZE_HORZ      , "_NET_WM_ACTION_MAXIMIZE_HORZ" },
362     { &_XA_NET_WM_ACTION_MAXIMIZE_VERT      , "_NET_WM_ACTION_MAXIMIZE_VERT" },
363     { &_XA_NET_WM_ACTION_MINIMIZE           , "_NET_WM_ACTION_MINIMIZE" },
364     { &_XA_NET_WM_ACTION_MOVE               , "_NET_WM_ACTION_MOVE" },
365     { &_XA_NET_WM_ACTION_RESIZE             , "_NET_WM_ACTION_RESIZE" },
366     { &_XA_NET_WM_ACTION_SHADE              , "_NET_WM_ACTION_SHADE" },
367     { &_XA_NET_WM_ACTION_STICK              , "_NET_WM_ACTION_STICK" },
368     { &_XA_NET_WM_ALLOWED_ACTIONS           , "_NET_WM_ALLOWED_ACTIONS" },
369     { &_XA_NET_WM_BYPASS_COMPOSITOR         , "_NET_WM_BYPASS_COMPOSITOR" },
370     { &_XA_NET_WM_DESKTOP                   , "_NET_WM_DESKTOP" },
371     { &_XA_NET_WM_FULL_PLACEMENT            , "_NET_WM_FULL_PLACEMENT" },
372     { &_XA_NET_WM_FULLSCREEN_MONITORS       , "_NET_WM_FULLSCREEN_MONITORS" },
373     { &_XA_NET_WM_HANDLED_ICONS             , "_NET_WM_HANDLED_ICONS" },
374     { &_XA_NET_WM_ICON_GEOMETRY             , "_NET_WM_ICON_GEOMETRY" },
375     { &_XA_NET_WM_ICON_NAME                 , "_NET_WM_ICON_NAME" },
376     { &_XA_NET_WM_ICON                      , "_NET_WM_ICON" },
377     { &_XA_NET_WM_MOVERESIZE                , "_NET_WM_MOVERESIZE" },
378     { &_XA_NET_WM_NAME                      , "_NET_WM_NAME" },
379     { &_XA_NET_WM_OPAQUE_REGION             , "_NET_WM_OPAQUE_REGION" },
380     { &_XA_NET_WM_PID                       , "_NET_WM_PID" },
381     { &_XA_NET_WM_PING                      , "_NET_WM_PING" },
382     { &_XA_NET_WM_STATE                     , "_NET_WM_STATE" },
383     { &_XA_NET_WM_STATE_ABOVE               , "_NET_WM_STATE_ABOVE" },
384     { &_XA_NET_WM_STATE_BELOW               , "_NET_WM_STATE_BELOW" },
385     { &_XA_NET_WM_STATE_DEMANDS_ATTENTION   , "_NET_WM_STATE_DEMANDS_ATTENTION" },
386     { &_XA_NET_WM_STATE_FOCUSED             , "_NET_WM_STATE_FOCUSED" },
387     { &_XA_NET_WM_STATE_FULLSCREEN          , "_NET_WM_STATE_FULLSCREEN" },
388     { &_XA_NET_WM_STATE_HIDDEN              , "_NET_WM_STATE_HIDDEN" },
389     { &_XA_NET_WM_STATE_MAXIMIZED_HORZ      , "_NET_WM_STATE_MAXIMIZED_HORZ" },
390     { &_XA_NET_WM_STATE_MAXIMIZED_VERT      , "_NET_WM_STATE_MAXIMIZED_VERT" },
391     { &_XA_NET_WM_STATE_MODAL               , "_NET_WM_STATE_MODAL" },
392     { &_XA_NET_WM_STATE_SHADED              , "_NET_WM_STATE_SHADED" },
393     { &_XA_NET_WM_STATE_SKIP_PAGER          , "_NET_WM_STATE_SKIP_PAGER" },
394     { &_XA_NET_WM_STATE_SKIP_TASKBAR        , "_NET_WM_STATE_SKIP_TASKBAR" },
395     { &_XA_NET_WM_STATE_STICKY              , "_NET_WM_STATE_STICKY" },
396     { &_XA_NET_WM_STRUT                     , "_NET_WM_STRUT" },
397     { &_XA_NET_WM_STRUT_PARTIAL             , "_NET_WM_STRUT_PARTIAL" },
398     { &_XA_NET_WM_SYNC_REQUEST              , "_NET_WM_SYNC_REQUEST" },
399     { &_XA_NET_WM_SYNC_REQUEST_COUNTER      , "_NET_WM_SYNC_REQUEST_COUNTER" },
400     { &_XA_NET_WM_USER_TIME                 , "_NET_WM_USER_TIME" },
401     { &_XA_NET_WM_USER_TIME_WINDOW          , "_NET_WM_USER_TIME_WINDOW" },
402     { &_XA_NET_WM_VISIBLE_ICON_NAME         , "_NET_WM_VISIBLE_ICON_NAME" },
403     { &_XA_NET_WM_VISIBLE_NAME              , "_NET_WM_VISIBLE_NAME" },
404     { &_XA_NET_WM_WINDOW_OPACITY            , "_NET_WM_WINDOW_OPACITY" },
405     { &_XA_NET_WM_WINDOW_TYPE               , "_NET_WM_WINDOW_TYPE" },
406     { &_XA_NET_WM_WINDOW_TYPE_COMBO         , "_NET_WM_WINDOW_TYPE_COMBO" },
407     { &_XA_NET_WM_WINDOW_TYPE_DESKTOP       , "_NET_WM_WINDOW_TYPE_DESKTOP" },
408     { &_XA_NET_WM_WINDOW_TYPE_DIALOG        , "_NET_WM_WINDOW_TYPE_DIALOG" },
409     { &_XA_NET_WM_WINDOW_TYPE_DND           , "_NET_WM_WINDOW_TYPE_DND" },
410     { &_XA_NET_WM_WINDOW_TYPE_DOCK          , "_NET_WM_WINDOW_TYPE_DOCK" },
411     { &_XA_NET_WM_WINDOW_TYPE_DROPDOWN_MENU , "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU" },
412     { &_XA_NET_WM_WINDOW_TYPE_MENU          , "_NET_WM_WINDOW_TYPE_MENU" },
413     { &_XA_NET_WM_WINDOW_TYPE_NORMAL        , "_NET_WM_WINDOW_TYPE_NORMAL" },
414     { &_XA_NET_WM_WINDOW_TYPE_NOTIFICATION  , "_NET_WM_WINDOW_TYPE_NOTIFICATION" },
415     { &_XA_NET_WM_WINDOW_TYPE_POPUP_MENU    , "_NET_WM_WINDOW_TYPE_POPUP_MENU" },
416     { &_XA_NET_WM_WINDOW_TYPE_SPLASH        , "_NET_WM_WINDOW_TYPE_SPLASH" },
417     { &_XA_NET_WM_WINDOW_TYPE_TOOLBAR       , "_NET_WM_WINDOW_TYPE_TOOLBAR" },
418     { &_XA_NET_WM_WINDOW_TYPE_TOOLTIP       , "_NET_WM_WINDOW_TYPE_TOOLTIP" },
419     { &_XA_NET_WM_WINDOW_TYPE_UTILITY       , "_NET_WM_WINDOW_TYPE_UTILITY" },
420     { &_XA_NET_WORKAREA                     , "_NET_WORKAREA" },
421 
422     { &_XA_CLIPBOARD                        , "CLIPBOARD" },
423     { &_XA_MANAGER                          , "MANAGER" },
424     { &_XA_XEMBED                           , "_XEMBED" },
425     { &_XA_XEMBED_INFO                      , "_XEMBED_INFO" },
426     { &_XA_TARGETS                          , "TARGETS" },
427     { &_XA_UTF8_STRING                      , "UTF8_STRING" },
428 
429     { &XA_XdndActionAsk                     , "XdndActionAsk" },
430     { &XA_XdndActionCopy                    , "XdndActionCopy" },
431     { &XA_XdndActionLink                    , "XdndActionLink" },
432     { &XA_XdndActionMove                    , "XdndActionMove" },
433     { &XA_XdndActionPrivate                 , "XdndActionPrivate" },
434     { &XA_XdndAware                         , "XdndAware" },
435     { &XA_XdndDrop                          , "XdndDrop" },
436     { &XA_XdndEnter                         , "XdndEnter" },
437     { &XA_XdndFinished                      , "XdndFinished" },
438     { &XA_XdndLeave                         , "XdndLeave" },
439     { &XA_XdndPosition                      , "XdndPosition" },
440     { &XA_XdndProxy                         , "XdndProxy" },
441     { &XA_XdndStatus                        , "XdndStatus" },
442 };
443 
initAtoms()444 void YXApplication::initAtoms() {
445     const int num_atoms = int ACOUNT(atom_info);
446 
447 #ifdef HAVE_XINTERNATOMS
448     char* names[num_atoms];
449     Atom xatoms[num_atoms];
450 
451     for (int i = 0; i < num_atoms; i++)
452         names[i] = const_cast<char *>(atom_info[i].name);
453 
454     XInternAtoms(xapp->display(), names, num_atoms, False, xatoms);
455 
456     for (int i = 0; i < num_atoms; i++)
457         *(atom_info[i].atom) = xatoms[i];
458 #else
459     for (int i = 0; i < num_atoms; i++)
460         *(atom_info[i].atom) = xapp->atom(atom_info[i].name);
461 #endif
462 
463     qsort(atom_info, size_t(num_atoms), sizeof(atom_info[0]), sortAtoms);
464     setAtomName(atomName);
465 }
466 
sortAtoms(const void * p1,const void * p2)467 int YXApplication::sortAtoms(const void* p1, const void* p2) {
468     const Atom a1 = *static_cast<const YAtomName*>(p1)->atom;
469     const Atom a2 = *static_cast<const YAtomName*>(p2)->atom;
470     return int(long(a1) - long(a2));
471 }
472 
atomName(Atom atom)473 const char* YXApplication::atomName(Atom atom) {
474     int lo = 0, hi = int ACOUNT(atom_info);
475     while (lo < hi) {
476         int pv = (lo + hi) / 2;
477         if (atom < *atom_info[pv].atom)
478             hi = pv;
479         else if (atom > *atom_info[pv].atom)
480             lo = pv + 1;
481         else
482             return atom_info[pv].name;
483     }
484     static char buf[32];
485     snprintf(buf, sizeof buf, "Atom(%lu)", atom);
486     return buf;
487 }
488 
initModifiers()489 void YXApplication::initModifiers() {
490     XModifierKeymap *xmk = XGetModifierMapping(xapp->display());
491     AltMask = MetaMask = WinMask = SuperMask = HyperMask =
492         NumLockMask = ScrollLockMask = ModeSwitchMask = 0;
493 
494     if (xmk) {
495         KeyCode *c = xmk->modifiermap;
496 
497         for (int m = 0; m < 8; m++)
498             for (int k = 0; k < xmk->max_keypermod; k++, c++) {
499                 if (*c == NoSymbol)
500                     continue;
501                 KeySym kc = XkbKeycodeToKeysym(xapp->display(), *c, 0, 0);
502                 if (kc == NoSymbol)
503                     kc = XkbKeycodeToKeysym(xapp->display(), *c, 0, 1);
504                 if (kc == XK_Num_Lock && NumLockMask == 0)
505                     NumLockMask = (1 << m);
506                 if (kc == XK_Scroll_Lock && ScrollLockMask == 0)
507                     ScrollLockMask = (1 << m);
508                 if ((kc == XK_Alt_L || kc == XK_Alt_R) && AltMask == 0)
509                     AltMask = (1 << m);
510                 if ((kc == XK_Meta_L || kc == XK_Meta_R) && MetaMask == 0)
511                     MetaMask = (1 << m);
512                 if ((kc == XK_Super_L || kc == XK_Super_R) && SuperMask == 0)
513                     SuperMask = (1 << m);
514                 if ((kc == XK_Hyper_L || kc == XK_Hyper_R) && HyperMask == 0)
515                     HyperMask = (1 << m);
516                 if ((kc == XK_Mode_switch || kc == XK_ISO_Level3_Shift) && ModeSwitchMask == 0)
517                     ModeSwitchMask = (1 << m);
518             }
519 
520         XFreeModifiermap(xmk);
521     }
522     if (MetaMask == AltMask)
523         MetaMask = 0;
524 
525     MSG(("alt:%d meta:%d super:%d hyper:%d win:%d mode:%d num:%d scroll:%d",
526          AltMask, MetaMask, SuperMask, HyperMask, WinMask, ModeSwitchMask,
527          NumLockMask, ScrollLockMask));
528 
529     // some hacks for "broken" modifier configurations
530     if (HyperMask == SuperMask)
531         HyperMask = 0;
532 
533     // this basically does what <0.9.13 versions did
534     if (AltMask != 0 && MetaMask == Mod1Mask) {
535         MetaMask = AltMask;
536         AltMask = Mod1Mask;
537     }
538 
539     if (AltMask == 0 && MetaMask != 0) {
540         if (MetaMask != Mod1Mask) {
541             AltMask = Mod1Mask;
542         }
543         else {
544             AltMask = MetaMask;
545             MetaMask = 0;
546         }
547     }
548 
549     if (AltMask == 0)
550         AltMask = Mod1Mask;
551 
552     if (ModeSwitchMask & (AltMask | MetaMask | SuperMask | HyperMask))
553         ModeSwitchMask = 0;
554 
555     PRECONDITION(xapp->AltMask != 0);
556     PRECONDITION(xapp->AltMask != ShiftMask);
557     PRECONDITION(xapp->AltMask != ControlMask);
558     PRECONDITION(xapp->AltMask != xapp->MetaMask);
559 
560     KeyMask =
561         ControlMask |
562         ShiftMask |
563         AltMask |
564         MetaMask |
565         SuperMask |
566         HyperMask |
567         ModeSwitchMask;
568 
569     ButtonMask =
570         Button1Mask |
571         Button2Mask |
572         Button3Mask |
573         Button4Mask |
574         Button5Mask;
575 
576     ButtonKeyMask = KeyMask | ButtonMask;
577 
578 #if 0
579     KeySym wl = XKeycodeToKeysym(app->display(), 115, 0);
580     KeySym wr = XKeycodeToKeysym(app->display(), 116, 0);
581 
582     if (wl == XK_Super_L) {
583     } else if (wl == XK_Meta_L) {
584     }
585 #endif
586     // this will do for now, but we should actualy check the keycodes
587     Win_L = Win_R = 0;
588 
589     if (SuperMask != 0) {
590         WinMask = SuperMask;
591 
592         Win_L = XK_Super_L;
593         Win_R = XK_Super_R;
594     }
595     MSG(("alt:%d meta:%d super:%d hyper:%d win:%d mode:%d num:%d scroll:%d",
596          AltMask, MetaMask, SuperMask, HyperMask, WinMask, ModeSwitchMask,
597          NumLockMask, ScrollLockMask));
598 
599 }
600 
hasControlAlt(unsigned state) const601 bool YXApplication::hasControlAlt(unsigned state) const {
602     return xapp->AltMask && hasbits((state & KeyMask), ControlMask | AltMask);
603 }
604 
hasWinMask(unsigned state) const605 bool YXApplication::hasWinMask(unsigned state) const {
606     return xapp->WinMask && hasbit((state & KeyMask), WinMask);
607 }
608 
dispatchEvent(YWindow * win,XEvent & xev)609 void YXApplication::dispatchEvent(YWindow *win, XEvent &xev) {
610     if (xev.type == KeyPress || xev.type == KeyRelease) {
611         YWindow *w = win;
612 
613         if (w && (fGrabWindow == nullptr || fGrabTree)) {
614             if (w->toplevel())
615                 w = w->toplevel();
616 
617             if (w->getFocusWindow())
618                 w = w->getFocusWindow();
619         }
620 
621         for (; w && (w->handleKey(xev.xkey) == false); w = w->parent()) {
622             if (fGrabTree && w == fXGrabWindow)
623                 break;
624         }
625     } else {
626         Window child;
627 
628         if (xev.type == MotionNotify) {
629             if (xev.xmotion.window != win->handle()) {
630                 if (XTranslateCoordinates(xapp->display(),
631                                           xev.xany.window, win->handle(),
632                                           xev.xmotion.x, xev.xmotion.y,
633                                           &xev.xmotion.x, &xev.xmotion.y, &child) == True)
634                     xev.xmotion.window = win->handle();
635                 else
636                     return ;
637             }
638         } else if (xev.type == ButtonPress || xev.type == ButtonRelease ||
639                    xev.type == EnterNotify || xev.type == LeaveNotify)
640         {
641             if (xev.xbutton.window != win->handle()) {
642                 if (XTranslateCoordinates(xapp->display(),
643                                           xev.xany.window, win->handle(),
644                                           xev.xbutton.x, xev.xbutton.y,
645                                           &xev.xbutton.x, &xev.xbutton.y, &child) == True)
646                     xev.xbutton.window = win->handle();
647                 else
648                     return ;
649             }
650         } else if (xev.type == KeyPress || xev.type == KeyRelease) {
651             if (xev.xkey.window != win->handle()) {
652                 if (XTranslateCoordinates(xapp->display(),
653                                           xev.xany.window, win->handle(),
654                                           xev.xkey.x, xev.xkey.y,
655                                           &xev.xkey.x, &xev.xkey.y, &child) == True)
656                     xev.xkey.window = win->handle();
657                 else
658                     return ;
659             }
660         }
661         win->handleEvent(xev);
662     }
663 }
664 
handleGrabEvent(YWindow * winx,XEvent & xev)665 void YXApplication::handleGrabEvent(YWindow *winx, XEvent &xev) {
666     struct {
667         YWindow *ptr;
668     } win = { winx };
669 
670     PRECONDITION(win.ptr != 0);
671     if (fGrabTree) {
672         if (xev.xbutton.subwindow != None) {
673             if ( ! windowContext.find(xev.xbutton.subwindow, &win.ptr))
674             {
675                 if (xev.type == EnterNotify || xev.type == LeaveNotify)
676                     win.ptr = nullptr;
677                 else
678                     win.ptr = fGrabWindow;
679             }
680         } else {
681             if ( ! windowContext.find(xev.xbutton.window, &win.ptr))
682             {
683                 if (xev.type == EnterNotify || xev.type == LeaveNotify)
684                     win.ptr = nullptr;
685                 else
686                     win.ptr = fGrabWindow;
687             }
688         }
689         if (win.ptr == nullptr)
690             return ;
691         {
692             YWindow *p = win.ptr;
693             for (; p; p = p->parent()) {
694                 if (p == fXGrabWindow)
695                     break;
696             }
697             if (p == nullptr) {
698                 if (xev.type == EnterNotify || xev.type == LeaveNotify)
699                     return ;
700                 else
701                     win.ptr = fGrabWindow;
702             }
703         }
704         if (xev.type == EnterNotify || xev.type == LeaveNotify)
705             if (win.ptr != fGrabWindow)
706                 return ;
707         if (fGrabWindow != fXGrabWindow)
708             win.ptr = fGrabWindow;
709     }
710     dispatchEvent(win.ptr, xev);
711 }
712 
replayEvent()713 void YXApplication::replayEvent() {
714     if (!fReplayEvent) {
715         fReplayEvent = true;
716         XAllowEvents(xapp->display(), ReplayPointer, CurrentTime);
717     }
718 }
719 
captureGrabEvents(YWindow * win)720 void YXApplication::captureGrabEvents(YWindow *win) {
721     if (fGrabWindow == fXGrabWindow && fGrabTree) {
722         fGrabWindow = win;
723     }
724 }
725 
releaseGrabEvents(YWindow * win)726 void YXApplication::releaseGrabEvents(YWindow *win) {
727     if (win == fGrabWindow && fGrabTree) {
728         fGrabWindow = fXGrabWindow;
729     }
730 }
731 
grabEvents(YWindow * win,Cursor ptr,unsigned long eventMask,bool grabMouse,bool grabKeyboard,bool grabTree)732 bool YXApplication::grabEvents(YWindow *win, Cursor ptr,
733         unsigned long eventMask, bool grabMouse, bool grabKeyboard, bool grabTree)
734 {
735     if (fGrabWindow || !win)
736         return false;
737 
738     fGrabTree = grabTree;
739     fGrabMouse = grabMouse;
740     if (grabMouse) {
741         int rc = XGrabPointer(display(), win->handle(), grabTree,
742                               eventMask, GrabModeSync, GrabModeAsync,
743                               None, ptr, CurrentTime);
744         if (rc) {
745             MSG(("grab status = %d\x7", rc));
746             return false;
747         }
748     }
749     else {
750         XChangeActivePointerGrab(display(), eventMask, ptr, CurrentTime);
751     }
752 
753     if (grabKeyboard) {
754         int rc = XGrabKeyboard(display(), win->handle(), grabTree,
755                                GrabModeSync, GrabModeAsync, CurrentTime);
756         if (rc) {
757             MSG(("grab status = %d\x7", rc));
758             if (grabMouse) {
759                 XUngrabPointer(display(), CurrentTime);
760                 fGrabMouse = false;
761             }
762             return false;
763         }
764     }
765     XAllowEvents(xapp->display(), SyncPointer, CurrentTime);
766 
767     fXGrabWindow = win;
768     fGrabWindow = win;
769     return true;
770 }
771 
releaseEvents()772 bool YXApplication::releaseEvents() {
773     if (fGrabWindow == nullptr)
774         return false;
775 
776     fGrabWindow = nullptr;
777     fXGrabWindow = nullptr;
778     fGrabTree = false;
779     if (fGrabMouse) {
780         XUngrabPointer(display(), CurrentTime);
781         fGrabMouse = false;
782     }
783     XUngrabKeyboard(display(), CurrentTime);
784 
785     return true;
786 }
787 
afterWindowEvent(XEvent &)788 void YXApplication::afterWindowEvent(XEvent & /*xev*/) {
789 }
790 
filterEvent(const XEvent & xev)791 bool YXApplication::filterEvent(const XEvent &xev) {
792     if (xev.type == MappingNotify) {
793         MSG(("MappingNotify"));
794         XMappingEvent xmapping = xev.xmapping;
795         XRefreshKeyboardMapping(&xmapping);
796 
797         while (0 < XPending(display())) {
798             XEvent event;
799             XNextEvent(display(), &event);
800             if (event.type == MappingNotify) {
801                 XRefreshKeyboardMapping(&event.xmapping);
802             } else {
803                 XPutBackEvent(display(), &event);
804                 break;
805             }
806         }
807 
808         initModifiers();
809 
810         desktop->grabKeys();
811         desktop->kbLayout();
812         return true;
813     }
814     return false;
815 }
816 
saveEventTime(const XEvent & xev)817 void YXApplication::saveEventTime(const XEvent &xev) {
818     switch (xev.type) {
819     case ButtonPress:
820     case ButtonRelease:
821         lastEventTime = xev.xbutton.time;
822         break;
823 
824     case MotionNotify:
825         lastEventTime = xev.xmotion.time;
826         break;
827 
828     case KeyPress:
829     case KeyRelease:
830         lastEventTime = xev.xkey.time;
831         break;
832 
833     case EnterNotify:
834     case LeaveNotify:
835         lastEventTime = xev.xcrossing.time;
836         break;
837 
838     case PropertyNotify:
839         lastEventTime = xev.xproperty.time;
840         break;
841 
842     case SelectionClear:
843         lastEventTime = xev.xselectionclear.time;
844         break;
845 
846     case SelectionRequest:
847         lastEventTime = xev.xselectionrequest.time;
848         break;
849 
850     case SelectionNotify:
851         lastEventTime = xev.xselection.time;
852         break;
853     }
854 }
855 
getEventTime(const char *) const856 Time YXApplication::getEventTime(const char *) const {
857     return lastEventTime;
858 }
859 
haveColormaps(Display * dpy)860 bool YXApplication::haveColormaps(Display* dpy) {
861     XVisualInfo pattern = { nullptr, None, DefaultScreen(dpy), 0, };
862     int i = 0, num = 0, mask = VisualScreenMask;
863     xsmart<XVisualInfo> info(XGetVisualInfo(dpy, mask, &pattern, &num));
864     for (; i < num && notbit(info[i].c_class, 1); ++i);
865     return i < num;
866 }
867 
visualForDepth(unsigned depth) const868 Visual* YXApplication::visualForDepth(unsigned depth) const {
869     Visual* vis =
870         depth == 32 ? fVisual32 :
871         depth == 24 ? fVisual24 :
872         depth == unsigned(DefaultDepth(display(), screen())) ?
873                  DefaultVisual(display(), screen()) :
874                  CopyFromParent;
875     return vis;
876 }
877 
colormapForDepth(unsigned depth) const878 Colormap YXApplication::colormapForDepth(unsigned depth) const {
879     Colormap cmap =
880         depth == 32 ? fColormap32 :
881         depth == 24 ? fColormap24 :
882         depth == unsigned(DefaultDepth(display(), screen())) ?
883                  DefaultColormap(display(), screen()) :
884                  CopyFromParent;
885     return cmap;
886 }
887 
colormapForVisual(Visual * visual) const888 Colormap YXApplication::colormapForVisual(Visual* visual) const {
889     Colormap cmap =
890         visual == fVisual32 ? fColormap32 :
891         visual == fVisual24 ? fColormap24 :
892         visual == DefaultVisual(display(), screen()) ?
893                   DefaultColormap(display(), screen()) :
894                   CopyFromParent;
895     return cmap;
896 }
897 
formatForDepth(unsigned depth) const898 XRenderPictFormat* YXApplication::formatForDepth(unsigned depth) const {
899     XRenderPictFormat* format =
900         depth == 32 ? fFormat32 :
901         depth == 24 ? fFormat24 :
902         nullptr;
903     return format;
904 }
905 
findFormat(int depth) const906 XRenderPictFormat* YXApplication::findFormat(int depth) const {
907     XRenderPictFormat* format = nullptr;
908     if (depth == 32)
909         format = XRenderFindStandardFormat(fDisplay, PictStandardARGB32);
910     if (depth == 24)
911         format = XRenderFindStandardFormat(fDisplay, PictStandardRGB24);
912     return format;
913 }
914 
findVisual(int depth) const915 Visual* YXApplication::findVisual(int depth) const {
916     Visual* found = nullptr;
917     XRenderPictFormat* pictFormat = findFormat(depth);
918     if (pictFormat) {
919         XVisualInfo pattern = {
920             found, None, fScreen, depth, TrueColor, None, None, None, 0, 8
921         };
922         int count = 0, mask = VisualDepthMask | VisualScreenMask |
923                               VisualClassMask | VisualBitsPerRGBMask;
924         xsmart<XVisualInfo> info(
925                 XGetVisualInfo(fDisplay, mask, &pattern, &count));
926         for (int i = 0; i < count && found == nullptr; ++i) {
927             XRenderPictFormat* format =
928                 XRenderFindVisualFormat(fDisplay, info[i].visual);
929             if (format == pictFormat) {
930                 found = info[i].visual;
931             }
932         }
933     }
934     if (found == nullptr) {
935         XVisualInfo pattern = {
936             found, 0, fScreen, depth, TrueColor, 0xff0000, 0xff00, 0xff, 0, 8
937         };
938         int mask = VisualScreenMask | VisualDepthMask | VisualClassMask
939                  | VisualRedMaskMask | VisualGreenMaskMask
940                  | VisualBlueMaskMask | VisualBitsPerRGBMask;
941         int count = 0;
942         xsmart<XVisualInfo> info(
943                 XGetVisualInfo(fDisplay, mask, &pattern, &count));
944         if (count && info) {
945             found = info->visual;
946         }
947     }
948     if (found == nullptr && depth == DefaultDepth(fDisplay, fScreen)) {
949         found = DefaultVisual(fDisplay, fScreen);
950     }
951     return found;
952 }
953 
cmapError(Display * disp,XErrorEvent * xerr)954 int YXApplication::cmapError(Display *disp, XErrorEvent *xerr) {
955     // Ignore create colormap error.
956     // This may occur with Xdmx for 32-bit visuals.
957     // Ignore for now, unless problems do show up.
958     return Success;
959 }
960 
getColormap(int depth) const961 Colormap YXApplication::getColormap(int depth) const {
962     Colormap cmap = None;
963     Visual* visual = depth == 32 ? fVisual32
964                    : depth == 24 ? fVisual24 : nullptr;
965     if (visual == DefaultVisual(fDisplay, fScreen)) {
966         cmap = DefaultColormap(fDisplay, fScreen);
967     }
968     else if (visual) {
969         XErrorHandler old = XSetErrorHandler(cmapError);
970         cmap = XCreateColormap(fDisplay, fRoot, visual, AllocNone);
971         XSync(fDisplay, False);
972         XSetErrorHandler(old);
973     }
974     else if (depth == DefaultDepth(fDisplay, fScreen)) {
975         cmap = DefaultColormap(fDisplay, fScreen);
976     }
977     return cmap;
978 }
979 
alert()980 void YXApplication::alert() {
981     XBell(display(), 100);
982 }
983 
setClipboardText(mstring data)984 void YXApplication::setClipboardText(mstring data) {
985     fClip->setData(data);
986 }
987 
dropClipboard()988 void YXApplication::dropClipboard() {
989     fClip = null;
990 }
991 
getHelpText()992 const char* YXApplication::getHelpText() {
993     return _(
994     "  -d, --display=NAME  NAME of the X server to use.\n"
995     "  --sync              Synchronize X11 commands.\n"
996     );
997 }
998 
999 const char*
parseArgs(int argc,char ** argv,const char * displayName)1000 YXApplication::parseArgs(int argc, char **argv, const char *displayName) {
1001     for (char ** arg = argv + 1; arg < argv + argc; ++arg) {
1002         if (**arg == '-') {
1003             char *value;
1004             if (is_help_switch(*arg)) {
1005                 print_help_exit(getHelpText());
1006             }
1007             else if (is_version_switch(*arg)) {
1008                 print_version_exit(VERSION);
1009             }
1010             else if (is_copying_switch(*arg)) {
1011                 print_copying_exit();
1012             }
1013             else if (GetArgument(value, "d", "display", arg, argv + argc)) {
1014                 if (isEmpty(displayName))
1015                     displayName = value;
1016             }
1017             else if (is_long_switch(*arg, "sync"))
1018                 synchronizeX11 = true;
1019             else if (is_long_switch(*arg, "alpha"))
1020                 alphaBlending = true;
1021         }
1022     }
1023 
1024     return displayName;
1025 }
1026 
openDisplay(const char * displayName)1027 Display* YXApplication::openDisplay(const char* displayName) {
1028     if (nonempty(displayName))
1029         setenv("DISPLAY", displayName, True);
1030     else
1031         displayName = getenv("DISPLAY");
1032 
1033     Display* display = XOpenDisplay(displayName);
1034     if (display == nullptr)
1035         die(1, _("Can't open display: %s. X must be running and $DISPLAY set."),
1036             displayName ? displayName : _("<none>"));
1037 
1038     if (synchronizeX11)
1039         XSynchronize(display, True);
1040 
1041     XSetErrorHandler(errorHandler);
1042 
1043     initExtensions(display);
1044 
1045     return display;
1046 }
1047 
YXApplication(int * argc,char *** argv,const char * displayName)1048 YXApplication::YXApplication(int *argc, char ***argv, const char *displayName):
1049     YApplication(argc, argv),
1050 
1051     fDisplay( openDisplay( parseArgs(*argc, *argv, displayName))),
1052     fScreen( DefaultScreen(fDisplay)),
1053     fRoot( RootWindow(fDisplay, fScreen)),
1054     fFormat32( findFormat(32)),
1055     fFormat24( findFormat(24)),
1056     fVisual32( findVisual(32)),
1057     fVisual24( findVisual(24)),
1058     fColormap32( getColormap(32)),
1059     fColormap24( getColormap(24)),
1060     fAlpha( alphaBlending && fVisual32 && fColormap32 ),
1061     fDepth( fAlpha ? 32 : fVisual24 ? 24 : DefaultDepth(fDisplay, fScreen)),
1062     fVisual( visualForDepth(fDepth)),
1063     fColormap( colormapForDepth(fDepth)),
1064     fHasColormaps( haveColormaps(display())),
1065     fBlack( BlackPixel(display(), screen())),
1066     fWhite( WhitePixel(display(), screen())),
1067 
1068     lastEventTime(CurrentTime),
1069     fPopup(nullptr),
1070     xfd(this),
1071     fXGrabWindow(nullptr),
1072     fGrabWindow(nullptr),
1073     fGrabTree(false),
1074     fGrabMouse(false),
1075     fReplayEvent(false)
1076 {
1077     xapp = this;
1078     xfd.registerPoll(ConnectionNumber(display()));
1079 
1080     new YDesktop(nullptr, root());
1081     extern void image_init();
1082     image_init();
1083 
1084     initAtoms();
1085     initModifiers();
1086 }
1087 
init(Display * dis,QueryFunc ext,QueryFunc ver)1088 void YExtension::init(Display* dis, QueryFunc ext, QueryFunc ver) {
1089     supported = (*ext)(dis, &eventBase, &errorBase)
1090              && (*ver)(dis, &versionMajor, &versionMinor);
1091     parameter = False;
1092 }
1093 
init(Display * dis,ExistFunc ext,ParamFunc ver)1094 void YExtension::init(Display* dis, ExistFunc ext, ParamFunc ver) {
1095     supported = (*ext)(dis)
1096              && (*ver)(dis, &versionMajor, &versionMinor, &parameter);
1097     eventBase = errorBase = 0;
1098 }
1099 
initExtensions(Display * dpy)1100 void YXApplication::initExtensions(Display* dpy) {
1101 
1102     composite.init(dpy, XCompositeQueryExtension, XCompositeQueryVersion);
1103     damage.init(dpy, XDamageQueryExtension, XDamageQueryVersion);
1104     fixes.init(dpy, XFixesQueryExtension, XFixesQueryVersion);
1105     render.init(dpy, XRenderQueryExtension, XRenderQueryVersion);
1106 
1107 #ifdef CONFIG_SHAPE
1108     shapes.init(dpy, XShapeQueryExtension, XShapeQueryVersion);
1109 #endif
1110 
1111 #ifdef CONFIG_XRANDR
1112     xrandr.init(dpy, XRRQueryExtension, XRRQueryVersion);
1113     xrandr.supported = (12 <= 10 * xrandr.versionMajor + xrandr.versionMinor);
1114 #endif
1115 
1116 #ifdef XINERAMA
1117     xinerama.init(dpy, XineramaQueryExtension, XineramaQueryVersion);
1118     xinerama.supported = (xinerama.supported && XineramaIsActive(dpy));
1119 #endif
1120 
1121     xshm.init(dpy, XShmQueryExtension, XShmQueryVersion);
1122 }
1123 
~YXApplication()1124 YXApplication::~YXApplication() {
1125     if (fColormap32)
1126         XFreeColormap(display(), fColormap32);
1127 
1128     xfd.unregisterPoll();
1129     XCloseDisplay(display());
1130     xapp = nullptr;
1131 }
1132 
handleXEvents()1133 bool YXApplication::handleXEvents() {
1134     const int prratio = 3;
1135     int retrieved = 0;
1136     for (; retrieved < XPending(display()); retrieved += prratio - 1) {
1137         XEvent xev;
1138 
1139         XNextEvent(display(), &xev);
1140 #ifdef DEBUG
1141         xeventcount++;
1142 #endif
1143         //msg("%d", xev.type);
1144 
1145         saveEventTime(xev);
1146 
1147 #if LOGEVENTS
1148         if (loggingEvents) {
1149             if (xev.type < LASTEvent)
1150                 logEvent(xev);
1151 #ifdef CONFIG_SHAPE
1152             else if (shapes.isEvent(xev.type, ShapeNotify))
1153                 logShape(xev);
1154 #endif
1155 #ifdef CONFIG_XRANDR
1156             else if (xrandr.isEvent(xev.type, RRScreenChangeNotify))
1157                 logRandrScreen(xev);
1158             else if (xrandr.isEvent(xev.type, RRNotify))
1159                 logRandrNotify(xev);
1160 #endif
1161         }
1162 #endif
1163 
1164         if (filterEvent(xev)) {
1165         } else {
1166             bool ge = xev.type == ButtonPress ||
1167                       xev.type == ButtonRelease ||
1168                       xev.type == MotionNotify ||
1169                       xev.type == KeyPress ||
1170                       xev.type == KeyRelease /*||
1171                       xev.type == EnterNotify ||
1172                       xev.type == LeaveNotify*/;
1173 
1174             fReplayEvent = false;
1175 
1176             if (fPopup && ge) {
1177                 handleGrabEvent(fPopup, xev);
1178             } else if (fGrabWindow && ge) {
1179                 handleGrabEvent(fGrabWindow, xev);
1180             } else {
1181                 handleWindowEvent(xev.xany.window, xev);
1182             }
1183             if (fGrabWindow) {
1184                 if (xev.type == ButtonPress ||
1185                     xev.type == ButtonRelease ||
1186                     xev.type == MotionNotify)
1187                 {
1188                     if (!fReplayEvent) {
1189                         XAllowEvents(xapp->display(), SyncPointer, CurrentTime);
1190                     }
1191                 }
1192             }
1193         }
1194         XFlush(display());
1195     }
1196     return retrieved > 0;
1197 }
1198 
handleIdle()1199 bool YXApplication::handleIdle() {
1200     return handleXEvents();
1201 }
1202 
handleWindowEvent(Window xwindow,XEvent & xev)1203 void YXApplication::handleWindowEvent(Window xwindow, XEvent &xev) {
1204     struct {
1205         YWindow *ptr;
1206     } window = { nullptr };
1207 
1208     if (windowContext.find(xwindow, &window.ptr))
1209     {
1210         if ((xev.type == KeyPress || xev.type == KeyRelease)
1211             && window.ptr->toplevel())
1212         {
1213             YWindow *w = window.ptr->toplevel();
1214 
1215             if (w->getFocusWindow())
1216                 w = w->getFocusWindow();
1217 
1218             dispatchEvent(w, xev);
1219         } else {
1220             window.ptr->handleEvent(xev);
1221         }
1222     } else {
1223         if (xev.type == MapRequest) {
1224             if (xev.xmaprequest.window != ignorable) {
1225                 // !!! java seems to do this ugliness
1226                 //YFrameWindow *f = getFrame(xev.xany.window);
1227                 TLOG(("APP BUG? mapRequest for window %lX "
1228                       "sent to destroyed frame %lX!",
1229                     xev.xmaprequest.window,
1230                     xev.xmaprequest.parent));
1231                 desktop->handleEvent(xev);
1232             }
1233         } else if (xev.type == ConfigureRequest) {
1234             if (xev.xconfigurerequest.window != ignorable) {
1235                 TLOG(("APP BUG? configureRequest for window %lX "
1236                       "sent to destroyed frame %lX!",
1237                     xev.xconfigurerequest.window,
1238                     xev.xconfigurerequest.parent));
1239                 desktop->handleEvent(xev);
1240             }
1241         }
1242         else if (xev.type == ClientMessage && desktop) {
1243             Atom mesg = xev.xclient.message_type;
1244             if (mesg == _XA_NET_REQUEST_FRAME_EXTENTS) {
1245                 desktop->handleEvent(xev);
1246             }
1247             else
1248             {
1249                 MSG(("Unknown client message %ld, win 0x%lX, data %ld,%ld",
1250                      mesg, xev.xclient.window,
1251                      xev.xclient.data.l[0], xev.xclient.data.l[1]));
1252             }
1253         }
1254         else if (xev.type != DestroyNotify) {
1255             MSG(("unknown window 0x%lX event=%d", xev.xany.window, xev.type));
1256         }
1257     }
1258     if (xev.type == KeyPress || xev.type == KeyRelease) ///!!!
1259         afterWindowEvent(xev);
1260 }
1261 
flushXEvents()1262 void YXApplication::flushXEvents() {
1263     XFlush(display());
1264 }
1265 
handleError(XErrorEvent * xev)1266 int YXApplication::handleError(XErrorEvent* xev) {
1267     return BadImplementation;
1268 }
1269 
errorHandler(Display * display,XErrorEvent * xev)1270 int YXApplication::errorHandler(Display* display, XErrorEvent* xev) {
1271     int rc = xapp->handleError(xev);
1272     if (rc == Success)
1273         return rc;
1274 
1275     XDBG if (xev->resourceid != ignorable) {
1276         char message[80], req[80], number[80];
1277 
1278         snprintf(number, sizeof number, "%d", xev->request_code);
1279         XGetErrorDatabaseText(display, "XRequest", number, "", req, sizeof req);
1280         if (req[0] == 0)
1281             snprintf(req, sizeof req, "[request_code=%d]", xev->request_code);
1282 
1283         if (XGetErrorText(display, xev->error_code, message, sizeof message))
1284             *message = '\0';
1285 
1286         tlog("X error %s(0x%lx): %s, #%lu, %+ld, %+ld.",
1287              req, xev->resourceid, message, xev->serial,
1288              long(NextRequest(display)) - long(xev->serial),
1289              long(LastKnownRequestProcessed(display)) - long(xev->serial));
1290 
1291 #if defined(DEBUG) || defined(PRECON)
1292         if (xapp->synchronized()) {
1293             switch (xev->request_code) {
1294                 case X_GetWindowAttributes:
1295                     break;
1296                 case X_GetImage:
1297                 case X_CreateGC:
1298                     show_backtrace();
1299                     break;
1300                 default:
1301                     show_backtrace();
1302                     break;
1303             }
1304         }
1305         else if (ONCE) {
1306             TLOG(("unsynchronized"));
1307         }
1308 #endif
1309     }
1310 
1311     if (rc == BadImplementation)
1312         xapp->exit(rc);
1313     return rc;
1314 }
1315 
send(XClientMessageEvent & ev,Window win,long mask) const1316 void YXApplication::send(XClientMessageEvent& ev, Window win, long mask) const {
1317     XSendEvent(display(), win, False, mask, reinterpret_cast<XEvent*>(&ev));
1318 }
1319 
parent(Window child) const1320 Window YXApplication::parent(Window child) const {
1321     Window paren = None;
1322     Window rootw = None;
1323     Window* data = nullptr;
1324     unsigned num = None;
1325     if (XQueryTree(display(), child, &rootw, &paren, &data, &num) && data) {
1326         XFree(data);
1327     }
1328     return paren;
1329 }
1330 
children(Window win,Window ** data,unsigned * num) const1331 bool YXApplication::children(Window win, Window** data, unsigned* num) const {
1332     Window rootw;
1333     Window paren;
1334     return XQueryTree(display(), win, &rootw, &paren, data, num);
1335 }
1336 
notifyRead()1337 void YXPoll::notifyRead() {
1338     owner()->handleXEvents();
1339 }
1340 
atomize()1341 void YAtom::atomize() {
1342     if (screen) {
1343         char buf[256];
1344         snprintf(buf, sizeof buf, "%s%d", name, xapp->screen());
1345         atom = xapp->atom(buf);
1346     } else {
1347         atom = xapp->atom(name);
1348     }
1349 }
1350 
operator Atom()1351 YAtom::operator Atom() {
1352     if (atom == None)
1353         atomize();
1354     return atom;
1355 }
1356 
YTextProperty(const char * str)1357 YTextProperty::YTextProperty(const char* str) {
1358     encoding = XA_STRING;
1359     format = 8;
1360     if (str) {
1361         nitems = strlen(str);
1362         value = (unsigned char *) malloc(1 + nitems);
1363         if (value) memcpy(value, str, 1 + nitems);
1364     } else {
1365         nitems = 0;
1366         value = nullptr;
1367     }
1368 }
1369 
~YTextProperty()1370 YTextProperty::~YTextProperty() {
1371     if (value) XFree(value);
1372 }
1373 
discard()1374 void YProperty::discard() {
1375     if (fData) {
1376         XFree(fData);
1377         fData = nullptr;
1378         fSize = None;
1379         fType = None;
1380     }
1381 }
1382 
update()1383 const YProperty& YProperty::update() {
1384     discard();
1385     int fmt = 0;
1386     if (XGetWindowProperty(xapp->display(), fWind, fProp, 0L, fLimit, fDelete,
1387                            fKind, &fType, &fmt, &fSize, &fMore, &fData) ==
1388         Success && fData && fSize && fmt == fBits && (fKind == fType || !fKind))
1389     {
1390     } else {
1391         discard();
1392     }
1393     return *this;
1394 }
1395 
append(void const * data,int count) const1396 void YProperty::append(void const* data, int count) const {
1397     unsigned char const* bytes = reinterpret_cast<unsigned char const*>(data);
1398     XChangeProperty(xapp->display(), fWind, fProp, fKind, fBits,
1399                     PropModeAppend, bytes, count);
1400 }
1401 
replace(void const * data,int count) const1402 void YProperty::replace(void const* data, int count) const {
1403     unsigned char const* bytes = reinterpret_cast<unsigned char const*>(data);
1404     XChangeProperty(xapp->display(), fWind, fProp, fKind, fBits,
1405                     PropModeReplace, bytes, count);
1406 }
1407 
1408 // vim: set sw=4 ts=4 et:
1409