1 /*
2  * IceWM
3  *
4  * Copyright (C) 1997-2003 Marko Macek
5  */
6 #include "config.h"
7 #define WMAPP
8 #include "appnames.h"
9 #define GUI_EVENT_NAMES
10 #include "guievent.h"
11 #include "yfull.h"
12 #include "wmprog.h"
13 #include "wmwinmenu.h"
14 #include "wmapp.h"
15 #include "wmframe.h"
16 #include "wmmgr.h"
17 #include "wmstatus.h"
18 #include "wmabout.h"
19 #include "wmdialog.h"
20 #include "wmconfig.h"
21 #include "wmwinlist.h"
22 #include "wmtaskbar.h"
23 #include "wmsession.h"
24 #include "wpixres.h"
25 #include "sysdep.h"
26 #include "ylocale.h"
27 #include "yprefs.h"
28 #include "prefs.h"
29 #include "udir.h"
30 #include "ascii.h"
31 #include "ycursor.h"
32 #include "yxcontext.h"
33 #ifdef CONFIG_XFREETYPE
34 #include <ft2build.h>
35 #include <X11/Xft/Xft.h>
36 #endif
37 #undef override
38 #include <X11/Xproto.h>
39 #include "ywordexp.h"
40 #include "intl.h"
41 
42 char const *ApplicationName("IceWM");
43 RebootShutdown rebootOrShutdown = Logout;
44 static bool initializing(true);
45 
46 YWMApp *wmapp;
47 YWindowManager *manager;
48 
49 YCursor YWMApp::leftPointer;
50 YCursor YWMApp::rightPointer;
51 YCursor YWMApp::movePointer;
52 YCursor YWMApp::sizeRightPointer;
53 YCursor YWMApp::sizeTopRightPointer;
54 YCursor YWMApp::sizeTopPointer;
55 YCursor YWMApp::sizeTopLeftPointer;
56 YCursor YWMApp::sizeLeftPointer;
57 YCursor YWMApp::sizeBottomLeftPointer;
58 YCursor YWMApp::sizeBottomPointer;
59 YCursor YWMApp::sizeBottomRightPointer;
60 YCursor YWMApp::scrollLeftPointer;
61 YCursor YWMApp::scrollRightPointer;
62 YCursor YWMApp::scrollUpPointer;
63 YCursor YWMApp::scrollDownPointer;
64 
65 lazy<MoveMenu> moveMenu;
66 lazy<TileMenu> tileMenu;
67 lazy<LayerMenu> layerMenu;
68 lazily<SharedWindowList> windowListMenu;
69 lazy<LogoutMenu> logoutMenu;
70 lazily<RootMenu> rootMenu;
71 
72 static bool replace_wm;
73 static bool post_preferences;
74 static bool show_extensions;
75 
registerProtocols1(char ** argv,int argc)76 static Window registerProtocols1(char **argv, int argc) {
77     long timestamp = CurrentTime;
78     YAtom wmSx("WM_S", true);
79 
80     Window current_wm = XGetSelectionOwner(xapp->display(), wmSx);
81 
82     if (current_wm != None) {
83         if (!replace_wm)
84             die(1, _("A window manager is already running, use --replace to replace it"));
85       XSetWindowAttributes attrs;
86       attrs.event_mask = StructureNotifyMask;
87       XChangeWindowAttributes (
88           xapp->display(), current_wm,
89           CWEventMask, &attrs);
90     }
91 
92     Window xroot = xapp->root();
93     Window xid =
94         XCreateSimpleWindow(xapp->display(), xroot,
95             0, 0, 1, 1, 0,
96             xapp->black(),
97             xapp->black());
98 
99     XSetSelectionOwner(xapp->display(), wmSx, xid, timestamp);
100 
101     if (XGetSelectionOwner(xapp->display(), wmSx) != xid)
102         die(1, _("Failed to become the owner of the %s selection"), wmSx.str());
103 
104     if (current_wm != None) {
105         XEvent event;
106         msg(_("Waiting to replace the old window manager"));
107         do {
108             XWindowEvent(xapp->display(), current_wm,
109                          StructureNotifyMask, &event);
110         } while (event.type != DestroyNotify);
111         msg(_("done."));
112     }
113 
114     static char wm_class[] = "IceWM";
115     static char wm_instance[] = "icewm";
116 
117     XClassHint class_hint = {
118         argv ? nullptr : wm_instance,
119         wm_class
120     };
121 
122     static char wm_name[] = "IceWM " VERSION " (" HOSTOS "/" HOSTCPU ")";
123 
124     Xutf8SetWMProperties(xapp->display(), xid, wm_name, nullptr,
125             argv, argc, nullptr, nullptr, &class_hint);
126 
127     XClientMessageEvent ev;
128 
129     memset(&ev, 0, sizeof(ev));
130     ev.type = ClientMessage;
131     ev.window = xroot;
132     ev.message_type = _XA_MANAGER;
133     ev.format = 32;
134     ev.data.l[0] = timestamp;
135     ev.data.l[1] = wmSx;
136     ev.data.l[2] = xid;
137 
138     xapp->send(ev, xroot, StructureNotifyMask);
139     return xid;
140 }
141 
registerWinProtocols(Window xid)142 static void registerWinProtocols(Window xid) {
143     Atom win_proto[] = {
144         _XA_WIN_ICONS,
145         _XA_WIN_LAYER,
146         _XA_WIN_PROTOCOLS,
147         _XA_WIN_TRAY,
148     };
149     int win_count = int ACOUNT(win_proto);
150     desktop->setProperty(_XA_WIN_PROTOCOLS, XA_ATOM, win_proto, win_count);
151 }
152 
registerNetProtocols(Window xid)153 static void registerNetProtocols(Window xid) {
154     Atom net_proto[] = {
155         _XA_NET_ACTIVE_WINDOW,
156         _XA_NET_CLIENT_LIST,
157         _XA_NET_CLIENT_LIST_STACKING,
158         _XA_NET_CLOSE_WINDOW,
159         _XA_NET_CURRENT_DESKTOP,
160         _XA_NET_DESKTOP_GEOMETRY,
161         _XA_NET_DESKTOP_LAYOUT,
162         _XA_NET_DESKTOP_NAMES,
163         _XA_NET_DESKTOP_VIEWPORT,
164         _XA_NET_FRAME_EXTENTS,
165         _XA_NET_MOVERESIZE_WINDOW,
166         _XA_NET_NUMBER_OF_DESKTOPS,
167 //      _XA_NET_PROPERTIES,
168         _XA_NET_REQUEST_FRAME_EXTENTS,
169         _XA_NET_RESTACK_WINDOW,
170         _XA_NET_SHOWING_DESKTOP,
171         _XA_NET_STARTUP_ID,
172 //      _XA_NET_STARTUP_INFO,
173 //      _XA_NET_STARTUP_INFO_BEGIN,
174         _XA_NET_SUPPORTED,
175         _XA_NET_SUPPORTING_WM_CHECK,
176         _XA_NET_SYSTEM_TRAY_MESSAGE_DATA,
177         _XA_NET_SYSTEM_TRAY_OPCODE,
178         _XA_NET_SYSTEM_TRAY_ORIENTATION,
179         _XA_NET_SYSTEM_TRAY_VISUAL,
180 //      _XA_NET_VIRTUAL_ROOTS,
181         _XA_NET_WM_ACTION_ABOVE,
182         _XA_NET_WM_ACTION_BELOW,
183         _XA_NET_WM_ACTION_CHANGE_DESKTOP,
184         _XA_NET_WM_ACTION_CLOSE,
185         _XA_NET_WM_ACTION_FULLSCREEN,
186         _XA_NET_WM_ACTION_MAXIMIZE_HORZ,
187         _XA_NET_WM_ACTION_MAXIMIZE_VERT,
188         _XA_NET_WM_ACTION_MINIMIZE,
189         _XA_NET_WM_ACTION_MOVE,
190         _XA_NET_WM_ACTION_RESIZE,
191         _XA_NET_WM_ACTION_SHADE,
192         _XA_NET_WM_ACTION_STICK,
193         _XA_NET_WM_ALLOWED_ACTIONS,
194 //      _XA_NET_WM_BYPASS_COMPOSITOR,
195         _XA_NET_WM_DESKTOP,
196 //      _XA_NET_WM_FULL_PLACEMENT,
197         _XA_NET_WM_FULLSCREEN_MONITORS,
198         _XA_NET_WM_HANDLED_ICONS,           // trivial support
199 //      _XA_NET_WM_ICON_GEOMETRY,
200         _XA_NET_WM_ICON_NAME,
201         _XA_NET_WM_ICON,
202         _XA_NET_WM_MOVERESIZE,
203         _XA_NET_WM_NAME,
204 //      _XA_NET_WM_OPAQUE_REGION,
205         _XA_NET_WM_PID,                     // trivial support
206         _XA_NET_WM_PING,
207         _XA_NET_WM_STATE,
208         _XA_NET_WM_STATE_ABOVE,
209         _XA_NET_WM_STATE_BELOW,
210         _XA_NET_WM_STATE_DEMANDS_ATTENTION,
211         _XA_NET_WM_STATE_FOCUSED,
212         _XA_NET_WM_STATE_FULLSCREEN,
213         _XA_NET_WM_STATE_HIDDEN,
214         _XA_NET_WM_STATE_MAXIMIZED_HORZ,
215         _XA_NET_WM_STATE_MAXIMIZED_VERT,
216         _XA_NET_WM_STATE_MODAL,
217         _XA_NET_WM_STATE_SHADED,
218         _XA_NET_WM_STATE_SKIP_PAGER,        // trivial support
219         _XA_NET_WM_STATE_SKIP_TASKBAR,
220         _XA_NET_WM_STATE_STICKY,            // trivial support
221         _XA_NET_WM_STRUT,
222         _XA_NET_WM_STRUT_PARTIAL,           // trivial support
223 //      _XA_NET_WM_SYNC_REQUEST,
224 //      _XA_NET_WM_SYNC_REQUEST_COUNTER,
225         _XA_NET_WM_USER_TIME,
226         _XA_NET_WM_USER_TIME_WINDOW,
227         _XA_NET_WM_VISIBLE_ICON_NAME,       // trivial support
228         _XA_NET_WM_VISIBLE_NAME,            // trivial support
229         _XA_NET_WM_WINDOW_OPACITY,
230         _XA_NET_WM_WINDOW_TYPE,
231         _XA_NET_WM_WINDOW_TYPE_COMBO,
232         _XA_NET_WM_WINDOW_TYPE_DESKTOP,
233         _XA_NET_WM_WINDOW_TYPE_DIALOG,
234         _XA_NET_WM_WINDOW_TYPE_DND,
235         _XA_NET_WM_WINDOW_TYPE_DOCK,
236         _XA_NET_WM_WINDOW_TYPE_DROPDOWN_MENU,
237         _XA_NET_WM_WINDOW_TYPE_MENU,
238         _XA_NET_WM_WINDOW_TYPE_NORMAL,
239         _XA_NET_WM_WINDOW_TYPE_NOTIFICATION,
240         _XA_NET_WM_WINDOW_TYPE_POPUP_MENU,
241         _XA_NET_WM_WINDOW_TYPE_SPLASH,
242         _XA_NET_WM_WINDOW_TYPE_TOOLBAR,
243         _XA_NET_WM_WINDOW_TYPE_TOOLTIP,
244         _XA_NET_WM_WINDOW_TYPE_UTILITY,
245         _XA_NET_WORKAREA
246     };
247     int net_count = int ACOUNT(net_proto);
248 
249     if ((showTaskBar & taskBarEnableSystemTray) == false) {
250         for (int k = net_count; 0 < k--; ) {
251             if (net_proto[k] == _XA_NET_SYSTEM_TRAY_MESSAGE_DATA ||
252                 net_proto[k] == _XA_NET_SYSTEM_TRAY_OPCODE ||
253                 net_proto[k] == _XA_NET_SYSTEM_TRAY_ORIENTATION ||
254                 net_proto[k] == _XA_NET_SYSTEM_TRAY_VISUAL)
255             {
256                 int keep = --net_count - k;
257                 if (keep > 0) {
258                     size_t size = keep * sizeof(Atom);
259                     memmove(&net_proto[k], &net_proto[k + 1], size);
260                 }
261             }
262         }
263     }
264 
265     desktop->setProperty(_XA_NET_SUPPORTED, XA_ATOM, net_proto, net_count);
266 }
267 
registerNetProperties(Window xid)268 static void registerNetProperties(Window xid) {
269     XChangeProperty(xapp->display(), xid,
270                     _XA_NET_SUPPORTING_WM_CHECK, XA_WINDOW, 32,
271                     PropModeReplace, (unsigned char *)&xid, 1);
272 
273     XID pid = getpid();
274 
275     XChangeProperty(xapp->display(), xid,
276                     _XA_NET_WM_PID, XA_CARDINAL, 32,
277                     PropModeReplace, (unsigned char *)&pid, 1);
278 
279     const char wmname[] = "IceWM " VERSION " (" HOSTOS "/" HOSTCPU ")";
280 
281     XChangeProperty(xapp->display(), xid,
282                     _XA_NET_WM_NAME, _XA_UTF8_STRING, 8,
283                     PropModeReplace, (unsigned char *)wmname,
284                     strnlen(wmname, sizeof(wmname)));
285 
286     desktop->setProperty(_XA_NET_SUPPORTING_WM_CHECK, XA_WINDOW, xid);
287 }
288 
registerProtocols2(Window xid)289 static void registerProtocols2(Window xid) {
290     registerWinProtocols(xid);
291     registerNetProtocols(xid);
292     registerNetProperties(xid);
293 }
294 
unregisterProtocols()295 void YWMApp::unregisterProtocols() {
296     if (managerWindow) {
297         YAtom wmSx("WM_S", true);
298         if (managerWindow == XGetSelectionOwner(xapp->display(), wmSx)) {
299             XSelectInput(xapp->display(), xapp->root(), None);
300             XDeleteProperty(xapp->display(), xapp->root(), _XA_WIN_PROTOCOLS);
301             XSetSelectionOwner(xapp->display(), wmSx, None, CurrentTime);
302         }
303         managerWindow = None;
304         xapp->sync();
305     }
306 }
307 
initIconSize()308 void YWMApp::initIconSize() {
309     XIconSize *is = XAllocIconSize();
310     if (is) {
311         unsigned sizes[] = {
312             menuIconSize,
313             smallIconSize,
314             largeIconSize,
315             hugeIconSize
316         };
317         unsigned count = 4;
318         for (unsigned i = 1; i < count; ++i) {
319             for (unsigned j = i; j > 0 && sizes[j-1] > sizes[j]; --j)
320                 swap(sizes[j], sizes[j-1]);
321         }
322         unsigned delta = 0;
323         for (unsigned i = 1; i < count; ++i) {
324             unsigned gap = sizes[i] - sizes[i - 1];
325             if (0 < gap && (delta == 0 || gap < delta))
326                 delta = gap;
327         }
328         is->min_width = is->min_height = int(sizes[0]);
329         is->max_width = is->max_height = int(sizes[count - 1]);
330         is->width_inc = is->height_inc = int(delta);
331         XSetIconSizes(xapp->display(), desktop->handle(), is, 1);
332         XFree(is);
333     }
334 }
335 
initIcons()336 void YWMApp::initIcons() {
337     defaultAppIcon = YIcon::getIcon("app");
338 }
339 
getDefaultAppIcon()340 ref<YIcon> YWMApp::getDefaultAppIcon() {
341     return defaultAppIcon;
342 }
343 
getCtrlAltDelete()344 CtrlAltDelete* YWMApp::getCtrlAltDelete() {
345     if (ctrlAltDelete == nullptr) {
346         ctrlAltDelete = new CtrlAltDelete(this, desktop);
347     }
348     return ctrlAltDelete;
349 }
350 
subdirs(const char * subdir,bool themeOnly,MStringArray & paths)351 void YWMApp::subdirs(const char* subdir, bool themeOnly, MStringArray& paths) {
352     if (resourcePaths.isEmpty()) {
353         upath privDir(YApplication::getPrivConfDir());
354         upath confDir(YApplication::getConfigDir());
355         upath libsDir(YApplication::getLibDir());
356         upath themeFile(themeName);
357         upath themeDir(themeFile.getExtension().isEmpty()
358                        ? themeFile : themeFile.parent());
359 
360         if (themeDir.isAbsolute()) {
361             if (privDir.dirExists()) {
362                 resourcePaths += privDir;
363                 themeOnlyPath += false;
364             }
365             if (themeDir.dirExists()) {
366                 resourcePaths += themeDir;
367                 themeOnlyPath += true;
368             }
369             if (confDir.dirExists()) {
370                 resourcePaths += confDir;
371                 themeOnlyPath += false;
372             }
373             if (libsDir.dirExists()) {
374                 resourcePaths += libsDir;
375                 themeOnlyPath += false;
376             }
377         } else {
378             const upath themes("/themes/");
379             const upath themesPlus(themes + themeDir);
380 
381             if (privDir.dirExists()) {
382                 upath plus(privDir + themesPlus);
383                 if (plus.dirExists()) {
384                     resourcePaths += plus;
385                     themeOnlyPath += true;
386                 }
387                 resourcePaths += privDir;
388                 themeOnlyPath += false;
389             }
390             if (confDir.dirExists()) {
391                 upath plus(confDir + themesPlus);
392                 if (plus.dirExists()) {
393                     resourcePaths += plus;
394                     themeOnlyPath += true;
395                 }
396                 resourcePaths += confDir;
397                 themeOnlyPath += false;
398             }
399             if (libsDir.dirExists()) {
400                 upath plus(libsDir + themesPlus);
401                 if (plus.dirExists()) {
402                     resourcePaths += plus;
403                     themeOnlyPath += true;
404                 }
405                 resourcePaths += libsDir;
406                 themeOnlyPath += false;
407             }
408         }
409         pathsTimer->setTimer(3210L, this, true);
410     }
411     for (int i = 0; i < resourcePaths.getCount(); ++i) {
412         if (themeOnly == false || themeOnlyPath[i] == true) {
413             if (isEmpty(subdir) ||
414                 upath(resourcePaths[i]).relative(subdir).isExecutable())
415             {
416                 paths += resourcePaths[i];
417             }
418         }
419     }
420 }
421 
freePointers()422 void YWMApp::freePointers() {
423     YCursor* ptrs[] = {
424         &leftPointer, &rightPointer, &movePointer,
425         &sizeRightPointer, &sizeTopRightPointer,
426         &sizeTopPointer, &sizeTopLeftPointer,
427         &sizeLeftPointer, &sizeBottomLeftPointer,
428         &sizeBottomPointer, &sizeBottomRightPointer,
429         &scrollLeftPointer, &scrollRightPointer,
430         &scrollUpPointer, &scrollDownPointer,
431     };
432     for (YCursor* pt : ptrs)
433         pt->discard();
434 }
435 
initPointers()436 void YWMApp::initPointers() {
437     struct {
438         YCursor* curp;
439         const char* name;
440         unsigned fallback;
441     } work[] = {
442         { &leftPointer           , "left.xpm",    XC_left_ptr },
443         { &rightPointer          , "right.xpm",   XC_right_ptr },
444         { &movePointer           , "move.xpm",    XC_fleur },
445         { &sizeRightPointer      , "sizeR.xpm",   XC_right_side },
446         { &sizeTopRightPointer   , "sizeTR.xpm",  XC_top_right_corner },
447         { &sizeTopPointer        , "sizeT.xpm",   XC_top_side },
448         { &sizeTopLeftPointer    , "sizeTL.xpm",  XC_top_left_corner },
449         { &sizeLeftPointer       , "sizeL.xpm",   XC_left_side },
450         { &sizeBottomLeftPointer , "sizeBL.xpm",  XC_bottom_left_corner },
451         { &sizeBottomPointer     , "sizeB.xpm",   XC_bottom_side },
452         { &sizeBottomRightPointer, "sizeBR.xpm",  XC_bottom_right_corner },
453         { &scrollLeftPointer     , "scrollL.xpm", XC_sb_left_arrow },
454         { &scrollRightPointer    , "scrollR.xpm", XC_sb_right_arrow },
455         { &scrollUpPointer       , "scrollU.xpm", XC_sb_up_arrow },
456         { &scrollDownPointer     , "scrollD.xpm", XC_sb_down_arrow },
457     };
458     unsigned size = ACOUNT(work);
459     unsigned mask = 0;
460     unsigned done = 0;
461     MStringArray dirs;
462     subdirs("cursors", false, dirs);
463     for (mstring& basedir : dirs) {
464         mstring cursors(basedir + "/cursors");
465         for (cdir dir(cursors); dir.nextExt(".xpm"); ) {
466             const char* nam = dir.entry();
467             for (unsigned i = 0; i < size; ++i) {
468                 if ((mask & (1 << i)) == 0 && strcmp(work[i].name, nam) == 0) {
469                     size_t len = cursors.length() + 2 + strlen(nam);
470                     char* path = new char[len];
471                     if (path)
472                         snprintf(path, len, "%s/%s", cursors.c_str(), nam);
473                     work[i].curp->init(path, work[i].fallback);
474                     mask |= (1 << i);
475                     if (++done == size)
476                         return;
477                 }
478             }
479         }
480     }
481     for (unsigned i = 0; i < size; ++i) {
482         if ((mask & (1 << i)) == 0) {
483             work[i].curp->init(nullptr, work[i].fallback);
484         }
485     }
486 }
487 
updatePopup()488 void LogoutMenu::updatePopup() {
489     if (itemCount())
490         return;
491 
492     if (showLogoutMenu) {
493         setShared(true); /// !!! get rid of this (refcount objects)
494         if (showLogoutSubMenu) {
495             addItem(_("_Logout"), -2, null, actionLogout, "logout");
496             addItem(_("_Cancel logout"), -2, null, actionCancelLogout,
497                     "cancel-logout")->setEnabled(false);
498             addSeparator();
499 
500             int const oldItemCount = itemCount();
501             if (canLock())
502                 addItem(_("Lock _Workstation"), -2, null, actionLock, "lock");
503             if (canShutdown(Reboot))
504                 addItem(_("Re_boot"), -2, null, actionReboot, "reboot");
505             if (canShutdown(Shutdown))
506                 addItem(_("Shut_down"), -2, null, actionShutdown, "shutdown");
507             if (couldRunCommand(suspendCommand))
508                 addItem(_("_Sleep mode"), -2, null, actionSuspend, "suspend");
509 
510             if (itemCount() != oldItemCount)
511                 addSeparator();
512 
513             addItem(_("Restart _Icewm"), -2, null, actionRestart, "restart");
514 
515             addItem(_("Restart _Xterm"), -2, null, actionRestartXterm, "xterm");
516 
517         }
518     }
519 }
520 
updatePopup()521 void LayerMenu::updatePopup() {
522     if (itemCount())
523         return;
524 
525     addItem(_("_Menu"),       -2, null, actionLayerMenu);
526     addItem(_("_Above Dock"), -2, null, actionLayerAboveDock);
527     addItem(_("_Dock"),       -2, null, actionLayerDock);
528     addItem(_("_OnTop"),      -2, null, actionLayerOnTop);
529     addItem(_("_Normal"),     -2, null, actionLayerNormal);
530     addItem(_("_Below"),      -2, null, actionLayerBelow);
531     addItem(_("D_esktop"),    -2, null, actionLayerDesktop);
532 }
533 
updatePopup()534 void MoveMenu::updatePopup() {
535     if (itemCount())
536         return;
537 
538     for (int w = 1; w <= workspaceCount; w++) {
539         char s[128];
540         snprintf(s, sizeof s, "%2d.  %s ", w, workspaceNames[w - 1]);
541         addItem(s, 1,
542                 w ==  1 ? KEY_NAME(gKeySysWorkspace1TakeWin)  :
543                 w ==  2 ? KEY_NAME(gKeySysWorkspace2TakeWin)  :
544                 w ==  3 ? KEY_NAME(gKeySysWorkspace3TakeWin)  :
545                 w ==  4 ? KEY_NAME(gKeySysWorkspace4TakeWin)  :
546                 w ==  5 ? KEY_NAME(gKeySysWorkspace5TakeWin)  :
547                 w ==  6 ? KEY_NAME(gKeySysWorkspace6TakeWin)  :
548                 w ==  7 ? KEY_NAME(gKeySysWorkspace7TakeWin)  :
549                 w ==  8 ? KEY_NAME(gKeySysWorkspace8TakeWin)  :
550                 w ==  9 ? KEY_NAME(gKeySysWorkspace9TakeWin)  :
551                 w == 10 ? KEY_NAME(gKeySysWorkspace10TakeWin) :
552                 w == 11 ? KEY_NAME(gKeySysWorkspace11TakeWin) :
553                 w == 12 ? KEY_NAME(gKeySysWorkspace12TakeWin) :
554                 "", workspaceActionMoveTo[w - 1]);
555     }
556 }
557 
updatePopup()558 void TileMenu::updatePopup() {
559     if (itemCount()) {
560         enableCommand(actionNull);
561         return;
562     }
563 
564     addItem(_("Left Half"),    -2, actionTileLeft, nullptr, "tileleft");
565     addItem(_("Right Half"),   -2, actionTileRight, nullptr, "tileright");
566     addItem(_("Top Half"),     -2, actionTileTop, nullptr, "tiletop");
567     addItem(_("Bottom Half"),  -2, actionTileBottom, nullptr, "tilebottom");
568     addSeparator();
569     addItem(_("Top Left"),     -2, actionTileTopLeft, nullptr,
570             "tiletopleft");
571     addItem(_("Top Right"),    -2, actionTileTopRight, nullptr,
572             "tiletopright");
573     addItem(_("Bottom Left"),  -2, actionTileBottomLeft, nullptr,
574             "tilebottomleft");
575     addItem(_("Bottom Right"), -2, actionTileBottomRight, nullptr,
576             "tilebottomright");
577     addItem(_("Center"),       -2, actionTileCenter, nullptr, "tilecenter");
578     addSeparator();
579     addItem(_("T_ile Horizontally"), -2,
580             KEY_NAME(gKeySysTileHorizontal), actionTileHorizontal);
581     addItem(_("Tile _Vertically"), -2,
582             KEY_NAME(gKeySysTileVertical), actionTileVertical);
583 }
584 
getWindowMenu()585 YMenu* YWMApp::getWindowMenu() {
586     if (windowMenu)
587         return windowMenu;
588 
589     windowMenu = new YMenu();
590     windowMenu->setShared(true);
591 
592     if (strchr(winMenuItems, 'r'))
593         windowMenu->addItem(_("_Restore"),  -2, KEY_NAME(gKeyWinRestore), actionRestore);
594     if (strchr(winMenuItems, 'm'))
595         windowMenu->addItem(_("_Move"),     -2, KEY_NAME(gKeyWinMove), actionMove);
596     if (strchr(winMenuItems, 's'))
597         windowMenu->addItem(_("_Size"),     -2, KEY_NAME(gKeyWinSize), actionSize);
598     if (strchr(winMenuItems, 'n'))
599         windowMenu->addItem(_("Mi_nimize"), -2, KEY_NAME(gKeyWinMinimize), actionMinimize);
600     if (strchr(winMenuItems, 'x')) {
601         windowMenu->addItem(_("Ma_ximize"), -2, KEY_NAME(gKeyWinMaximize), actionMaximize);
602         windowMenu->addItem(_("Maximize_Vert"), -2, KEY_NAME(gKeyWinMaximizeVert), actionMaximizeVert);
603         windowMenu->addItem(_("MaximizeHori_z"), -2, KEY_NAME(gKeyWinMaximizeHoriz), actionMaximizeHoriz);
604     }
605     if (strchr(winMenuItems,'f') && allowFullscreen)
606         windowMenu->addItem(_("_Fullscreen"), -2, KEY_NAME(gKeyWinFullscreen), actionFullscreen);
607 
608     if (strchr(winMenuItems, 'h'))
609         windowMenu->addItem(_("_Hide"),     -2, KEY_NAME(gKeyWinHide), actionHide);
610     if (strchr(winMenuItems, 'u'))
611         windowMenu->addItem(_("Roll_up"),   -2, KEY_NAME(gKeyWinRollup), actionRollup);
612     if (strchr(winMenuItems, 'a') ||
613         strchr(winMenuItems,'l') ||
614         strchr(winMenuItems,'y') ||
615         strchr(winMenuItems,'t'))
616         windowMenu->addSeparator();
617     if (strchr(winMenuItems, 'a'))
618         windowMenu->addItem(_("R_aise"),    -2, KEY_NAME(gKeyWinRaise), actionRaise);
619     if (strchr(winMenuItems, 'l'))
620         windowMenu->addItem(_("_Lower"),    -2, KEY_NAME(gKeyWinLower), actionLower);
621     if (strchr(winMenuItems, 'y'))
622         windowMenu->addSubmenu(_("La_yer"), -2, layerMenu);
623 
624     if (strchr(winMenuItems, 'y') && strchr(winMenuItems, 't'))
625         windowMenu->addSubmenu(_("Tile"), -2, tileMenu);
626 
627     if (strchr(winMenuItems, 't') && workspaceCount > 1) {
628         windowMenu->addSeparator();
629         windowMenu->addSubmenu(_("Move _To"), -2, moveMenu);
630         windowMenu->addItem(_("Occupy _All"), -2, KEY_NAME(gKeyWinOccupyAll), actionOccupyAllOrCurrent);
631     }
632 
633     /// this should probably go away, cause fullscreen will do mostly the same thing
634 #if DO_NOT_COVER_OLD
635     if (!limitByDockLayer)
636         windowMenu->addItem(_("Limit _Workarea"), -2, null, actionDoNotCover);
637 #endif
638 
639     if (strchr(winMenuItems, 'i') && taskBarShowTray)
640         windowMenu->addItem(_("Tray _icon"), -2, null, actionToggleTray);
641 
642     if (strchr(winMenuItems, 'c') || strchr(winMenuItems, 'k'))
643         windowMenu->addSeparator();
644     if (strchr(winMenuItems, 'c'))
645         windowMenu->addItem(_("_Close"), -2, KEY_NAME(gKeyWinClose), actionClose);
646     if (strchr(winMenuItems, 'k'))
647         windowMenu->addItem(_("_Kill Client"), -2, null, actionKill);
648     if (strchr(winMenuItems, 'w')) {
649         windowMenu->addSeparator();
650         windowMenu->addItem(_("_Window list"), -2, KEY_NAME(gKeySysWindowList), actionWindowList);
651     }
652 
653     return windowMenu;
654 }
655 
handleTimer(YTimer * timer)656 bool YWMApp::handleTimer(YTimer *timer) {
657     if (timer == errorTimer) {
658         errorTimer = null;
659         if (errorRequestCode == X_SetInputFocus && errorFrame != nullptr) {
660             if (errorFrame == manager->getFocus()) {
661                 if (errorFrame->client()) {
662                     errorFrame->client()->testDestroyed();
663                 }
664                 manager->setFocus(nullptr);
665                 manager->focusLastWindow();
666             }
667         }
668         errorRequestCode = Success;
669     }
670     else if (timer == splashTimer) {
671         splashTimer = null;
672         splashWindow = null;
673     }
674     else if (timer == pathsTimer) {
675         resourcePaths.clear();
676         themeOnlyPath.clear();
677         pathsTimer = null;
678     }
679 
680     return false;
681 }
682 
handleError(XErrorEvent * xev)683 int YWMApp::handleError(XErrorEvent *xev) {
684 
685     if (initializing &&
686         xev->request_code == X_ChangeWindowAttributes &&
687         xev->error_code == BadAccess)
688     {
689         msg(_("Another window manager already running, exiting..."));
690         ::exit(1);
691     }
692 
693     if (xev->error_code == BadWindow) {
694         YWindow* ywin = windowContext.find(xev->resourceid);
695         if (ywin) {
696             if (ywin->destroyed())
697                 return Success;
698             else
699                 ywin->setDestroyed();
700         }
701         if (xev->request_code == X_SetInputFocus) {
702             errorRequestCode = xev->request_code;
703             errorFrame = manager->getFocus();
704             if (errorFrame)
705                 errorTimer->setTimer(0, this, true);
706         }
707     }
708     if (xev->request_code == X_GetWindowAttributes) {
709         return Success;
710     }
711 
712     return BadRequest;
713 }
714 
715 #ifdef DEBUG
dumpZorder(const char * oper,YFrameWindow * w,YFrameWindow * a)716 void dumpZorder(const char *oper, YFrameWindow *w, YFrameWindow *a) {
717     msg("--- %s ", oper);
718     for (YFrameWindow *p = manager->top(w->getActiveLayer()); p; p = p->next()) {
719         if (p && p->client()) {
720             mstring cs(p->client()->windowTitle());
721             msg(" %c %c 0x%lX: %s", (p == w) ? '*' : ' ',  (p == a) ? '#' : ' ', p->client()->handle(), cs.c_str());
722         } else
723             msg("?? 0x%lX", p->handle());
724         PRECONDITION(p->next() != p);
725         PRECONDITION(p->prev() != p);
726         if (p->next()) {
727             PRECONDITION(p->next()->prev() == p);
728         }
729     }
730 }
731 #endif
732 
runRestart(const char * path,char * const * args)733 void YWMApp::runRestart(const char *path, char *const *args) {
734     XSelectInput(xapp->display(), desktop->handle(), 0);
735     XFlush(xapp->display());
736     ///!!! problem with repeated SIGHUP for restart...
737     resetSignals();
738 
739     closeFiles();
740 
741     if (path) {
742         if (args) {
743             execvp(path, args);
744         } else {
745             execlp(path, path, nullptr);
746         }
747     } else {
748         if (mainArgv[0][0] == '/' ||
749             (strchr(mainArgv[0], '/') != nullptr &&
750              access(mainArgv[0], X_OK) == 0))
751         {
752             execv(mainArgv[0], mainArgv);
753             fail("execv %s", mainArgv[0]);
754         }
755         execvp(ICEWMEXE, mainArgv);
756         fail("execvp %s", ICEWMEXE);
757     }
758 
759     xapp->alert();
760 
761     if (manager && desktop && desktop->getEventMask()) {
762         XSelectInput(xapp->display(), desktop->handle(), desktop->getEventMask());
763     } else {
764         die(13, _("Could not restart: %s\nDoes $PATH lead to %s?"),
765              strerror(errno), path ? path : ICEWMEXE);
766     }
767 }
768 
restartClient(const char * cpath,char * const * cargs)769 void YWMApp::restartClient(const char *cpath, char *const *cargs) {
770     csmart path(newstr(cpath));
771     YStringArray sargs((const char**) cargs);
772     char *const *args = (cargs == nullptr) ? nullptr : sargs.getCArray();
773 
774     signalGuiEvent(geRestart);
775     manager->unmanageClients();
776     unregisterProtocols();
777 
778     runRestart(path, args);
779 
780     // icesm knows how to restart.
781     if (notifyParent && notifiedParent && kill(notifiedParent, 0) == 0)
782         ::exit(ICESM_EXIT_RESTART);
783 
784     /* somehow exec failed, try to recover */
785     managerWindow = registerProtocols1(mainArgv, mainArgc);
786     registerProtocols2(managerWindow);
787     manager->manageClients();
788 }
789 
runProgram(const char * path,const char * const * args,int fd)790 int YWMApp::runProgram(const char *path, const char *const *args, int fd) {
791     mstring command;
792     YTraceProg trace;
793     if (trace.tracing()) {
794         command = path;
795         if (nonempty(*args)) {
796             for (int i = 1; args[i]; ++i) {
797                 command = mstring(command, " ", args[i]);
798             }
799         }
800         trace.init(command);
801     }
802     return YApplication::runProgram(path, args, fd);
803 }
804 
runOnce(const char * resource,long * pid,const char * path,char * const * args)805 void YWMApp::runOnce(const char *resource, long *pid,
806                      const char *path, char *const *args)
807 {
808     if (0 < *pid && mapClientByPid(resource, *pid))
809         return;
810 
811     if (mapClientByResource(resource, pid))
812         return;
813 
814     *pid = runProgram(path, args);
815 }
816 
runCommand(const char * cmdline)817 void YWMApp::runCommand(const char *cmdline) {
818     const char shell[] = "&();<>`{}|";
819     wordexp_t exp = {};
820     if (strpbrk(cmdline, shell) == nullptr &&
821         wordexp(cmdline, &exp, WRDE_NOCMD) == 0)
822     {
823         runProgram(exp.we_wordv[0], exp.we_wordv);
824         wordfree(&exp);
825     }
826     else {
827         char const * argv[] = { "/bin/sh", "-c", cmdline, nullptr };
828         runProgram(argv[0], argv);
829     }
830 }
831 
runCommandOnce(const char * resource,const char * cmdline,long * pid)832 void YWMApp::runCommandOnce(const char *resource, const char *cmdline, long *pid) {
833     if (0 < *pid && mapClientByPid(resource, *pid))
834         return;
835 
836     if (mapClientByResource(resource, pid))
837         return;
838 
839     const char shell[] = "&();<>`{}|";
840     wordexp_t exp = {};
841     if (strpbrk(cmdline, shell) == nullptr &&
842         wordexp(cmdline, &exp, WRDE_NOCMD) == 0)
843     {
844         *pid = runProgram(exp.we_wordv[0], exp.we_wordv);
845         wordfree(&exp);
846     }
847     else {
848         char const *const argv[] = { "/bin/sh", "-c", cmdline, nullptr };
849         *pid = runProgram(argv[0], argv);
850     }
851 }
852 
mapClientByPid(const char * resource,long pid)853 bool YWMApp::mapClientByPid(const char* resource, long pid) {
854     if (isEmpty(resource))
855         return false;
856 
857     bool found = false;
858 
859     for (YFrameIter frame = manager->focusedIterator(); ++frame; ) {
860         long tmp = 0;
861         if (frame->client()->getNetWMPid(&tmp) && tmp == pid) {
862             if (frame->client()->classHint()->match(resource)) {
863                 frame->setWorkspace(manager->activeWorkspace());
864                 frame->activateWindow(true);
865                 found = true;
866                 break;
867             }
868         }
869     }
870 
871     return found;
872 }
873 
mapClientByResource(const char * resource,long * pid)874 bool YWMApp::mapClientByResource(const char* resource, long *pid) {
875     if (isEmpty(resource))
876         return false;
877 
878     Window win(manager->findWindow(resource));
879     if (win) {
880         YFrameWindow* frame(manager->findFrame(win));
881         if (frame) {
882             frame->setWorkspace(manager->activeWorkspace());
883             frame->activateWindow(true);
884             frame->client()->getNetWMPid(pid);
885         }
886         else {
887             XMapRaised(xapp->display(), win);
888         }
889         return true;
890     }
891     return false;
892 }
893 
setFocusMode(FocusModel mode)894 void YWMApp::setFocusMode(FocusModel mode) {
895     focusMode = mode;
896     initFocusMode();
897     WMConfig::setDefaultFocus(mode);
898 }
899 
actionPerformed(YAction action,unsigned int)900 void YWMApp::actionPerformed(YAction action, unsigned int /*modifiers*/) {
901     if (action == actionLogout) {
902         doLogout(Logout);
903     } else if (action == actionCancelLogout) {
904         cancelLogout();
905     } else if (action == actionLock) {
906         runCommand(lockCommand);
907     } else if (action == actionShutdown) {
908         doLogout(Shutdown);
909     } else if (action == actionSuspend) {
910         runCommand(suspendCommand);
911     } else if (action == actionReboot) {
912         doLogout(Reboot);
913     } else if (action == actionRestart) {
914 #if defined(DEBUG) || defined(PRECON)
915         // Prefer a return from main for cleanup checking; icesm restarts.
916         if (notifyParent && notifiedParent && kill(notifiedParent, 0) == 0)
917             this->exit(ICESM_EXIT_RESTART);
918         else
919 #endif
920             restartClient(nullptr, nullptr);
921     }
922     else if (action == actionRestartXterm) {
923         if (fRestartMsgBox) {
924             fRestartMsgBox->unmanage();
925         }
926         fRestartMsgBox = new YMsgBox(YMsgBox::mbBoth,
927                                      _("Confirm Restart as Terminal"),
928                                      _("Unmanage all applications and restart\n"
929                                       "as a terminal. Proceed?"),
930                                      this, "xterm");
931     }
932     else if (action == actionRun) {
933         runCommand(runDlgCommand);
934     } else if (action == actionExit) {
935         exit(0);
936     } else if (action == actionFocusClickToFocus) {
937         setFocusMode(FocusClick);
938     } else if (action == actionFocusMouseSloppy) {
939         setFocusMode(FocusSloppy);
940     } else if (action == actionFocusExplicit) {
941         setFocusMode(FocusExplicit);
942     } else if (action == actionFocusMouseStrict) {
943         setFocusMode(FocusStrict);
944     } else if (action == actionFocusQuietSloppy) {
945         setFocusMode(FocusQuiet);
946     } else if (action == actionFocusCustom) {
947         setFocusMode(FocusCustom);
948     } else if (action == actionRefresh) {
949         refreshDesktop();
950     } else if (action == actionAbout) {
951         if (aboutDlg == nullptr)
952             aboutDlg = new AboutDlg(this);
953         if (aboutDlg)
954             aboutDlg->showFocused();
955     }
956     else if (action == actionAboutClose) {
957         if (aboutDlg) {
958             manager->unmanageClient(aboutDlg);
959             aboutDlg = nullptr;
960         }
961     }
962     else if (action == actionTileVertical) {
963         manager->tileWindows(true);
964     }
965     else if (action == actionTileHorizontal) {
966         manager->tileWindows(false);
967     }
968     else if (action == actionArrange) {
969         manager->arrangeWindows();
970     }
971     else if (action == actionArrangeIcons) {
972         manager->arrangeIcons();
973     }
974     else if (action == actionHideAll) {
975         manager->actionWindows(action);
976     }
977     else if (action == actionMinimizeAll) {
978         manager->actionWindows(action);
979     }
980     else if (action == actionShowDesktop) {
981         manager->toggleDesktop();
982     }
983     else if (action == actionCascade) {
984         manager->cascadeWindows();
985     }
986     else if (action == actionUndoArrange) {
987         manager->undoArrange();
988     }
989     else if (action == actionWindowList) {
990         if (windowList->visible())
991             windowList->handleClose();
992         else
993             windowList->showFocused(-1, -1);
994     } else if (action == actionWinOptions) {
995         loadWinOptions(findConfigFile("winoptions"));
996     } else if (action == actionReloadKeys) {
997         keyProgs.clear();
998         MenuLoader(this, this, this).loadMenus(findConfigFile("keys"), nullptr);
999         if (manager && !initializing) {
1000             if (manager->wmState() == YWindowManager::wmRUNNING) {
1001                 manager->grabKeys();
1002             }
1003         }
1004     } else if (action == actionCollapseTaskbar && taskBar) {
1005         taskBar->handleCollapseButton();
1006     } else {
1007         for (int w = 0; w < workspaceCount; w++) {
1008             if (workspaceActionActivate[w] == action) {
1009                 manager->activateWorkspace(w);
1010                 return ;
1011             }
1012         }
1013     }
1014 }
1015 
initFocusCustom()1016 void YWMApp::initFocusCustom() {
1017     cfoption focus_prefs[] = {
1018         OBV("ClickToFocus",              &clickFocus,                ""),
1019         OBV("FocusOnAppRaise",           &focusOnAppRaise,           ""),
1020         OBV("RequestFocusOnAppRaise",    &requestFocusOnAppRaise,    ""),
1021         OBV("RaiseOnFocus",              &raiseOnFocus,              ""),
1022         OBV("FocusOnClickClient",        &focusOnClickClient,        ""),
1023         OBV("RaiseOnClickClient",        &raiseOnClickClient,        ""),
1024         OBV("FocusChangesWorkspace",     &focusChangesWorkspace,     ""),
1025         OBV("FocusCurrentWorkspace",     &focusCurrentWorkspace,     ""),
1026         OBV("FocusOnMap",                &focusOnMap,                ""),
1027         OBV("FocusOnMapTransient",       &focusOnMapTransient,       ""),
1028         OBV("FocusOnMapTransientActive", &focusOnMapTransientActive, ""),
1029         OK0()
1030     };
1031     YConfig(focus_prefs).load(configFile).loadOverride();
1032 }
1033 
initFocusMode()1034 void YWMApp::initFocusMode() {
1035     switch (focusMode) {
1036 
1037     case FocusCustom: /* custom */
1038         initFocusCustom();
1039         break;
1040 
1041     case FocusClick: /* click to focus */
1042         clickFocus = true;
1043         // focusOnAppRaise = false;
1044         requestFocusOnAppRaise = true;
1045         raiseOnFocus = true;
1046         raiseOnClickClient = true;
1047         focusOnMap = true;
1048         mapInactiveOnTop = true;
1049         focusChangesWorkspace = false;
1050         focusOnMapTransient = false;
1051         focusOnMapTransientActive = true;
1052         break;
1053 
1054     case FocusSloppy:  /* sloppy mouse focus */
1055         clickFocus = false;
1056         // focusOnAppRaise = false;
1057         requestFocusOnAppRaise = true;
1058         raiseOnFocus = false;
1059         raiseOnClickClient = true;
1060         focusOnMap = true;
1061         mapInactiveOnTop = true;
1062         focusChangesWorkspace = false;
1063         focusOnMapTransient = false;
1064         focusOnMapTransientActive = true;
1065         break;
1066 
1067     case FocusExplicit: /* explicit focus */
1068         clickFocus = true;
1069         // focusOnAppRaise = false;
1070         requestFocusOnAppRaise = false;
1071         raiseOnFocus = false;
1072         raiseOnClickClient = false;
1073         focusOnMap = false;
1074         mapInactiveOnTop = true;
1075         focusChangesWorkspace = false;
1076         focusOnMapTransient = false;
1077         focusOnMapTransientActive = true;
1078         break;
1079 
1080     case FocusStrict:  /* strict mouse focus */
1081         clickFocus = false;
1082         // focusOnAppRaise = false;
1083         requestFocusOnAppRaise = false;
1084         raiseOnFocus = true;
1085         raiseOnClickClient = true;
1086         focusOnMap = false;
1087         mapInactiveOnTop = false;
1088         focusChangesWorkspace = false;
1089         focusOnMapTransient = false;
1090         focusOnMapTransientActive = true;
1091         break;
1092 
1093     case FocusQuiet:  /* quiet sloppy focus */
1094         clickFocus = false;
1095         // focusOnAppRaise = false;
1096         requestFocusOnAppRaise = false;
1097         raiseOnFocus = false;
1098         raiseOnClickClient = true;
1099         focusOnMap = true;
1100         mapInactiveOnTop = true;
1101         focusChangesWorkspace = false;
1102         focusOnMapTransient = false;
1103         focusOnMapTransientActive = true;
1104         break;
1105 
1106     default:
1107         warn("Erroneous focus mode %d.", focusMode);
1108     }
1109 }
1110 
loadFocusMode()1111 FocusModel YWMApp::loadFocusMode() {
1112     const char* focusMode = nullptr;
1113     cfoption focus_prefs[] = {
1114         OSV("FocusMode", &focusMode,
1115             "Focus mode (0=custom, 1=click, 2=sloppy"
1116             ", 3=explicit, 4=strict, 5=quiet)"),
1117         OK0()
1118     };
1119     YConfig(focus_prefs).load("focus_mode");
1120     FocusModel result = FocusClick;
1121     if (focusMode) {
1122         static const pair<FocusModel, const char*> models[] = {
1123             { FocusCustom,   "custom"   },
1124             { FocusClick,    "click"    },
1125             { FocusSloppy,   "sloppy"   },
1126             { FocusExplicit, "explicit" },
1127             { FocusStrict,   "strict"   },
1128             { FocusQuiet,    "quiet"    },
1129         };
1130         if (ASCII::isDigit(*focusMode)) {
1131             int mode = atoi(focusMode);
1132             for (auto model : models) {
1133                 if (mode == model.left) {
1134                     result = model.left;
1135                     break;
1136                 }
1137             }
1138         }
1139         else {
1140             mstring mode(mstring(focusMode).lower());
1141             for (auto model : models) {
1142                 if (mode == model.right) {
1143                     result = model.left;
1144                     break;
1145                 }
1146             }
1147         }
1148         delete[] const_cast<char *>(focusMode);
1149     }
1150     return result;
1151 }
1152 
showExtensions()1153 static void showExtensions() {
1154     pair<const char*, YExtension*> pairs[] = {
1155         { "composite", &composite },
1156         { "damage",    &damage    },
1157         { "fixes",     &fixes     },
1158         { "render",    &render    },
1159         { "shapes",    &shapes    },
1160         { "xrandr",    &xrandr    },
1161         { "xinerama",  &xinerama  },
1162         { "xshm",      &xshm      },
1163     };
1164     printf("[name]   [ver] [ev][err]\n");
1165     for (auto ext : pairs) {
1166         const char* s = ext.left;
1167         YExtension* x = ext.right;
1168         if (x->versionMajor | x->versionMinor) {
1169             printf("%-9s %d.%-2d", s, x->versionMajor, x->versionMinor);
1170             if (x->eventBase | x->errorBase) {
1171                 printf(" (%2d, %3d)", x->eventBase, x->errorBase);
1172             }
1173             else if (x == &xshm && x->parameter) {
1174                 printf(" pixmaps");
1175             }
1176             printf("\n");
1177         }
1178         if (!x->supported) {
1179             printf("%-9s unsupported\n", s);
1180         }
1181     }
1182 }
1183 
restartWM(const char * displayName,const char * overrideTheme)1184 static int restartWM(const char* displayName, const char* overrideTheme) {
1185     Display* display = XOpenDisplay(displayName);
1186     if (display) {
1187         if (nonempty(overrideTheme)) {
1188             WMConfig::setDefaultTheme(overrideTheme);
1189         }
1190         XClientMessageEvent message = {
1191             ClientMessage, 0UL, False, nullptr, DefaultRootWindow(display),
1192             XInternAtom(display, "_ICEWM_ACTION", False), 32,
1193         };
1194         message.data.l[0] = CurrentTime;
1195         message.data.l[1] = ICEWM_ACTION_RESTARTWM;
1196         XSendEvent(display, DefaultRootWindow(display), False,
1197                    SubstructureNotifyMask, (XEvent *) &message);
1198         XSync(display, False);
1199         XCloseDisplay(display);
1200         return EXIT_SUCCESS;
1201     }
1202     else {
1203         msg(_("Can't open display: %s. X must be running and $DISPLAY set."),
1204             displayName ? displayName : _("<none>"));
1205         return EXIT_FAILURE;
1206     }
1207 }
1208 
YWMApp(int * argc,char *** argv,const char * displayName,bool notifyParent,const char * splashFile,const char * configFile,const char * overrideTheme)1209 YWMApp::YWMApp(int *argc, char ***argv, const char *displayName,
1210                 bool notifyParent, const char *splashFile,
1211                 const char *configFile, const char *overrideTheme) :
1212     YSMApplication(argc, argv, displayName),
1213     mainArgv(*argv),
1214     mainArgc(*argc),
1215     configFile(configFile),
1216     notifyParent(notifyParent),
1217     notifiedParent(0),
1218     fLogoutMsgBox(nullptr),
1219     fRestartMsgBox(nullptr),
1220     aboutDlg(nullptr),
1221     ctrlAltDelete(nullptr),
1222     windowMenu(nullptr),
1223     errorRequestCode(0),
1224     errorFrame(nullptr),
1225     splashWindow(splash(splashFile)),
1226     focusMode(loadFocusMode()),
1227     managerWindow(None)
1228 {
1229     wmapp = this;
1230     YIcon::iconResourceLocator = this;
1231 
1232     WMConfig::loadConfiguration(configFile);
1233     WMConfig::loadThemeConfiguration();
1234     WMConfig::loadConfiguration("prefoverride");
1235     if (focusMode != FocusCustom)
1236         initFocusMode();
1237 
1238     DEPRECATE(warpPointer == true);
1239     DEPRECATE(focusRootWindow == true);
1240     DEPRECATE(replayMenuCancelClick == true);
1241     //DEPRECATE(manualPlacement == true);
1242     //DEPRECATE(strongPointerFocus == true);
1243     DEPRECATE(showPopupsAbovePointer == true);
1244     DEPRECATE(considerHorizBorder == true);
1245     DEPRECATE(considerVertBorder == true);
1246     DEPRECATE(sizeMaximized == true);
1247     DEPRECATE(dontRotateMenuPointer == false);
1248     DEPRECATE(lowerOnClickWhenRaised == true);
1249 
1250     catchSignal(SIGINT);
1251     catchSignal(SIGTERM);
1252     catchSignal(SIGQUIT);
1253     catchSignal(SIGHUP);
1254     catchSignal(SIGCHLD);
1255     catchSignal(SIGUSR2);
1256     catchSignal(SIGPIPE);
1257 
1258     actionPerformed(actionWinOptions, 0);
1259     actionPerformed(actionReloadKeys, 0);
1260 
1261     initPointers();
1262 
1263     if (post_preferences)
1264         WMConfig::printPrefs(focusMode, wmapp_preferences);
1265     if (show_extensions)
1266         showExtensions();
1267 
1268     delete desktop;
1269 
1270     managerWindow = registerProtocols1(*argv, *argc);
1271 
1272     manager = new YWindowManager(this, this, this, nullptr, root());
1273     PRECONDITION(desktop != nullptr);
1274 
1275     registerProtocols2(managerWindow);
1276 
1277     initIcons();
1278     initIconSize();
1279     WPixRes::initPixmaps(this);
1280 
1281     if (scrollBarWidth == 0) {
1282         switch(wmLook) {
1283             case lookWarp4:
1284                 scrollBarWidth = 14;
1285                 break;
1286 
1287             case lookMotif:
1288             case lookGtk:
1289                 scrollBarWidth = 15;
1290                 break;
1291 
1292             case lookNice:
1293             case lookWin95:
1294             case lookWarp3:
1295             case lookPixmap:
1296                 scrollBarWidth = 16;
1297                 break;
1298 
1299             case lookFlat:
1300             case lookMetal:
1301                 scrollBarWidth = 17;
1302                 break;
1303         }
1304     }
1305 
1306     if (scrollBarHeight == 0) {
1307         switch(wmLook) {
1308             case lookWarp4:
1309                 scrollBarHeight = 20;
1310                 break;
1311 
1312             case lookMotif:
1313             case lookGtk:
1314                 scrollBarHeight = scrollBarWidth;
1315                 break;
1316 
1317             case lookNice:
1318             case lookWin95:
1319             case lookWarp3:
1320             case lookPixmap:
1321                 scrollBarHeight = scrollBarWidth;
1322                 break;
1323 
1324             case lookFlat:
1325             case lookMetal:
1326                 scrollBarHeight = scrollBarWidth;
1327                 break;
1328         }
1329     }
1330 
1331     manager->initWorkspaces();
1332 
1333     manager->grabKeys();
1334 
1335     manager->setupRootProxy();
1336 
1337 #ifdef CONFIG_SESSION
1338     if (haveSessionManager())
1339         loadWindowInfo();
1340 #endif
1341 
1342     initializing = false;
1343 }
1344 
~YWMApp()1345 YWMApp::~YWMApp() {
1346     if (fLogoutMsgBox) {
1347         fLogoutMsgBox->unmanage();
1348         fLogoutMsgBox = nullptr;
1349     }
1350     if (fRestartMsgBox) {
1351         fRestartMsgBox->unmanage();
1352         fRestartMsgBox = nullptr;
1353     }
1354     if (aboutDlg) {
1355         manager->unmanageClient(aboutDlg);
1356         aboutDlg = nullptr;
1357     }
1358 
1359     delete ctrlAltDelete; ctrlAltDelete = nullptr;
1360     delete taskBar; taskBar = nullptr;
1361 
1362     if (statusMoveSize)
1363         statusMoveSize = null;
1364     if (statusWorkspace)
1365         statusWorkspace = null;
1366 
1367     if (rootMenu._ptr()) {
1368         rootMenu->setShared(false);
1369         rootMenu = null;
1370     }
1371     windowList = null;
1372 
1373     if (windowMenu) {
1374         windowMenu->setShared(false);
1375         delete windowMenu; windowMenu = nullptr;
1376     }
1377 
1378     // shared menus last
1379     if (logoutMenu) {
1380         logoutMenu->setShared(false);
1381         logoutMenu = null;
1382     }
1383     if (windowListMenu._ptr()) {
1384         windowListMenu->setShared(false);
1385         windowListMenu = null;
1386     }
1387     layerMenu = null;
1388     moveMenu = null;
1389     tileMenu = null;
1390 
1391     keyProgs.clear();
1392     workspaces.reset();
1393     WPixRes::freePixmaps();
1394 
1395     extern void clearFontCache();
1396     clearFontCache();
1397 
1398     YConfig::freeConfig(wmapp_preferences);
1399 
1400     XFlush(display());
1401     unsetenv("DISPLAY");
1402     alarm(1);
1403     wmapp = nullptr;
1404     YIcon::iconResourceLocator = nullptr;
1405 }
1406 
mainLoop()1407 int YWMApp::mainLoop() {
1408     signalGuiEvent(geStartup);
1409     manager->manageClients();
1410 
1411     if (notifyParent) {
1412         notifiedParent = getppid();
1413         if (kill(notifiedParent, SIGUSR1)) {
1414             notifiedParent = 0;
1415             notifyParent = false;
1416             fail("notify parent");
1417         }
1418     }
1419 
1420     int rc = super::mainLoop();
1421     signalGuiEvent(geShutdown);
1422     manager->unmanageClients();
1423     unregisterProtocols();
1424     YIcon::freeIcons();
1425     WMConfig::freeConfiguration();
1426     defOptions = null;
1427     hintOptions = null;
1428 
1429     return rc;
1430 }
1431 
handleSignal(int sig)1432 void YWMApp::handleSignal(int sig) {
1433     switch (sig) {
1434     case SIGINT:
1435     case SIGTERM:
1436         actionPerformed(actionExit, 0);
1437         break;
1438 
1439     case SIGQUIT:
1440         actionPerformed(actionLogout, 0);
1441         break;
1442 
1443     case SIGHUP:
1444         actionPerformed(actionRestart, 0);
1445         break;
1446 
1447     case SIGUSR2:
1448         tlog("logEvents %s", boolstr(toggleLogEvents()));
1449         break;
1450 
1451     case SIGPIPE:
1452         if (ferror(stdout) || ferror(stderr))
1453             this->exit(1);
1454         break;
1455 
1456     default:
1457         YApplication::handleSignal(sig);
1458         break;
1459     }
1460 }
1461 
handleIdle()1462 bool YWMApp::handleIdle() {
1463     static int qbits;
1464     bool busy = YSMApplication::handleIdle();
1465 
1466     if ((QLength(display()) >> qbits) > 0) {
1467         ++qbits;
1468     }
1469     else if (taskBar == nullptr && showTaskBar) {
1470         createTaskBar();
1471         busy = true;
1472     }
1473     else if (splashWindow) {
1474         splashWindow = null;
1475         splashTimer = null;
1476     }
1477     else if (taskBar) {
1478         taskBar->relayoutNow();
1479     }
1480     return busy;
1481 }
1482 
signalGuiEvent(GUIEvent ge)1483 void YWMApp::signalGuiEvent(GUIEvent ge) {
1484     guiSignaler->signal(ge);
1485 }
1486 
filterEvent(const XEvent & xev)1487 bool YWMApp::filterEvent(const XEvent &xev) {
1488     if (xev.type == SelectionClear) {
1489         if (xev.xselectionclear.window == managerWindow) {
1490             manager->unmanageClients();
1491             unregisterProtocols();
1492             exit(0);
1493         }
1494     }
1495     return YSMApplication::filterEvent(xev);
1496 }
1497 
afterWindowEvent(XEvent & xev)1498 void YWMApp::afterWindowEvent(XEvent &xev) {
1499     static XEvent lastKeyEvent = { 0 };
1500 
1501     if (xev.type == KeyRelease && lastKeyEvent.type == KeyPress) {
1502         KeySym k1 = XkbKeycodeToKeysym(xapp->display(), xev.xkey.keycode, 0, 0);
1503         unsigned int m1 = KEY_MODMASK(lastKeyEvent.xkey.state);
1504         KeySym k2 = XkbKeycodeToKeysym(xapp->display(), lastKeyEvent.xkey.keycode, 0, 0);
1505 
1506         if (m1 == 0 && xapp->WinMask && win95keys) {
1507             if (k1 == xapp->Win_L && k2 == xapp->Win_L) {
1508                 manager->popupStartMenu(desktop);
1509             }
1510             else if (k1 == xapp->Win_R && k2 == xapp->Win_R) {
1511                 manager->doWMAction(ICEWM_ACTION_WINDOWLIST, true);
1512             }
1513         }
1514     }
1515 
1516     if (xev.type == KeyPress ||
1517         xev.type == KeyRelease ||
1518         xev.type == ButtonPress ||
1519         xev.type == ButtonRelease)
1520         lastKeyEvent = xev;
1521 }
1522 
print_usage(const char * argv0)1523 static void print_usage(const char *argv0) {
1524     const char *usage_client_id =
1525 #ifdef CONFIG_SESSION
1526              _("  --client-id=ID      Client id to use when contacting session manager.\n");
1527 #else
1528              "";
1529 #endif
1530     const char *usage_debug =
1531 #ifdef DEBUG
1532              _("\n"
1533              "  --debug             Print generic debug messages.\n"
1534              "  --debug-z           Print debug messages regarding window stacking.\n");
1535 #else
1536              "";
1537 #endif
1538 
1539     const char *usage_preferences =
1540              _("\n"
1541              "  -a, --alpha         Use a 32-bit visual for translucency.\n"
1542              "  -c, --config=FILE   Load preferences from FILE.\n"
1543              "  -t, --theme=FILE    Load theme from FILE.\n"
1544              "  -s, --splash=IMAGE  Briefly show IMAGE on startup.\n"
1545              "  -p, --postpreferences  Print preferences after all processing.\n"
1546              "  --rewrite-preferences  Update an existing preferences file.\n"
1547              "  --trace=conf,icon   Trace paths used to load configuration.\n"
1548              );
1549 
1550     printf(_("Usage: %s [OPTIONS]\n"
1551              "Starts the IceWM window manager.\n"
1552              "\n"
1553              "Options:\n"
1554              "  -d, --display=NAME  NAME of the X server to use.\n"
1555              "%s"
1556              "  --sync              Synchronize X11 commands.\n"
1557              "%s"
1558              "\n"
1559              "  -V, --version       Prints version information and exits.\n"
1560              "  -h, --help          Prints this usage screen and exits.\n"
1561              "%s"
1562              "\n"
1563              "  --replace           Replace an existing window manager.\n"
1564              "  -r, --restart       Tell the running icewm to restart itself.\n"
1565              "\n"
1566              "  --configured        Print the compile time configuration.\n"
1567              "  --directories       Print the configuration directories.\n"
1568              "  -l, --list-themes   Print a list of all available themes.\n"
1569              "\n"
1570              "Environment variables:\n"
1571              "  ICEWM_PRIVCFG=PATH  Directory for user configuration files,\n"
1572              "                      \"$XDG_CONFIG_HOME/icewm\" if exists or\n"
1573              "                      \"$HOME/.icewm\" by default.\n"
1574              "  DISPLAY=NAME        Name of the X server to use.\n"
1575              "  MAIL=URL            Location of your mailbox.\n"
1576              "\n"
1577              "To report bugs, support requests, comments please visit:\n"
1578              "%s\n\n"),
1579              argv0,
1580              usage_client_id,
1581              usage_preferences,
1582              usage_debug,
1583              PACKAGE_BUGREPORT[0] ? PACKAGE_BUGREPORT :
1584              PACKAGE_URL[0] ? PACKAGE_URL :
1585              "https://ice-wm.org/");
1586     exit(0);
1587 }
1588 
print_themes_list()1589 static void print_themes_list() {
1590     themeName = nullptr;
1591     MStringArray resources;
1592     resources += YApplication::getPrivConfDir();
1593     resources += YApplication::getConfigDir();
1594     resources += YApplication::getLibDir();
1595     for (mstring& path : resources) {
1596         upath subdir(path + "/themes/");
1597         for (sdir dir(subdir); dir.next(); ) {
1598             upath thmp(subdir + dir.entry());
1599             if (thmp.dirExists()) {
1600                 for (sdir thmdir(thmp); thmdir.nextExt(".theme"); ) {
1601                     upath theme(thmp + thmdir.entry());
1602                     puts(theme.string());
1603                 }
1604             }
1605         }
1606     }
1607     exit(0);
1608 }
1609 
print_confdir(const char * name,const char * path)1610 static void print_confdir(const char *name, const char *path) {
1611     printf("%s=%s\n", name, path);
1612 }
1613 
print_directories(const char * argv0)1614 static void print_directories(const char *argv0) {
1615     upath priv(YApplication::getPrivConfDir());
1616     printf(_("%s configuration directories:\n"), argv0);
1617     print_confdir("PrivConfDir", priv.string());
1618     print_confdir("CFGDIR", CFGDIR);
1619     print_confdir("LIBDIR", LIBDIR);
1620     print_confdir("LOCDIR", LOCDIR);
1621     print_confdir("DOCDIR", DOCDIR);
1622     exit(0);
1623 }
1624 
print_configured(const char * argv0)1625 static void print_configured(const char *argv0) {
1626     static const char compile_time_configured_options[] =
1627     /* Sorted alphabetically: */
1628 #ifdef ENABLE_ALSA
1629     " alsa"
1630 #endif
1631 #ifdef ENABLE_AO
1632     " ao"
1633 #endif
1634 #ifdef CONFIG_COREFONTS
1635     " corefonts"
1636 #endif
1637 #ifdef DEBUG
1638     " debug"
1639 #endif
1640 #ifdef CONFIG_FDO_MENUS
1641     " fdomenus"
1642 #endif
1643 #ifdef CONFIG_FRIBIDI
1644     " fribidi"
1645 #endif
1646 #ifdef CONFIG_GDK_PIXBUF_XLIB
1647     " gdkpixbuf"
1648 #endif
1649 #ifdef CONFIG_I18N
1650     " i18n"
1651 #endif
1652 #ifdef CONFIG_IMLIB2
1653     " imlib2"
1654 #endif
1655 #ifdef CONFIG_LIBICONV
1656     " libiconv"
1657 #endif
1658 #ifdef CONFIG_LIBJPEG
1659     " libjpeg"
1660 #endif
1661 #ifdef CONFIG_LIBPNG
1662     " libpng"
1663 #endif
1664 #ifdef CONFIG_LIBRSVG
1665     " librsvg"
1666 #endif
1667 #ifdef CONFIG_XPM
1668     " libxpm"
1669 #endif
1670 #ifdef LOGEVENTS
1671     " logevents"
1672 #endif
1673 #ifdef ENABLE_NLS
1674     " nls"
1675 #endif
1676 #ifdef ENABLE_OSS
1677     " oss"
1678 #endif
1679 #ifdef CONFIG_SESSION
1680     " session"
1681 #endif
1682 #ifdef CONFIG_SHAPE
1683     " shape"
1684 #endif
1685 #ifdef CONFIG_UNICODE_SET
1686     " unicodeset"
1687 #endif
1688 #ifdef CONFIG_XFREETYPE
1689     " xfreetype" QUOTE(CONFIG_XFREETYPE)
1690 #endif
1691 #ifdef XINERAMA
1692     " xinerama"
1693 #endif
1694 #ifdef CONFIG_XRANDR
1695     " xrandr"
1696 #endif
1697     "\n";
1698     printf(_("%s configured options:%s\n"), argv0,
1699             compile_time_configured_options);
1700     exit(0);
1701 }
1702 
loadStartup(const char * configFile)1703 static void loadStartup(const char* configFile)
1704 {
1705     rightToLeft = YLocale::RTL();
1706     leftToRight = !rightToLeft;
1707 
1708     YConfig(wmapp_preferences).load(configFile);
1709 
1710     YXApplication::alphaBlending |= alphaBlending;
1711     YXApplication::synchronizeX11 |= synchronizeX11;
1712     if (tracingModules && YTrace::tracingConf() == nullptr) {
1713         YTrace::tracing(tracingModules);
1714     }
1715 
1716     unsigned last = ACOUNT(wmapp_preferences) - 2;
1717     YConfig(wmapp_preferences + last).load("theme");
1718 }
1719 
main(int argc,char ** argv)1720 int main(int argc, char **argv) {
1721     YLocale locale;
1722     bool restart_wm(false);
1723     bool log_events(false);
1724     bool rewrite_prefs(false);
1725     bool notify_parent(false);
1726     const char* configFile(nullptr);
1727     const char* displayName(nullptr);
1728     const char* overrideTheme(nullptr);
1729 
1730     for (char ** arg = argv + 1; arg < argv + argc; ++arg) {
1731         if (**arg == '-') {
1732             char *value(nullptr);
1733             if (GetArgument(value, "c", "config", arg, argv+argc))
1734                 configFile = value;
1735             else if (GetArgument(value, "t", "theme", arg, argv+argc))
1736                 overrideTheme = value;
1737             else if (is_switch(*arg, "p", "postpreferences"))
1738                 post_preferences = true;
1739             else if (is_long_switch(*arg, "rewrite-preferences"))
1740                 rewrite_prefs = true;
1741             else if (is_long_switch(*arg, "extensions"))
1742                 show_extensions = true;
1743             else
1744 #ifdef DEBUG
1745             if (is_long_switch(*arg, "debug"))
1746                 debug = true;
1747             else if (is_long_switch(*arg, "debug-z"))
1748                 debug_z = true;
1749             else
1750 #endif
1751             if (is_switch(*arg, "r", "restart"))
1752                 restart_wm = true;
1753             else if (is_long_switch(*arg, "replace"))
1754                 replace_wm = true;
1755             else if (is_long_switch(*arg, "notify"))
1756                 notify_parent = true;
1757             else if (is_long_switch(*arg, "configured"))
1758                 print_configured(argv[0]);
1759             else if (is_long_switch(*arg, "directories"))
1760                 print_directories(argv[0]);
1761             else if (is_switch(*arg, "l", "list-themes"))
1762                 print_themes_list();
1763             else if (is_help_switch(*arg))
1764                 print_usage(my_basename(argv[0]));
1765             else if (is_version_switch(*arg))
1766                 print_version_exit(VERSION);
1767             else if (is_copying_switch(*arg))
1768                 print_copying_exit();
1769             else if (is_long_switch(*arg, "sync"))
1770                 YXApplication::synchronizeX11 = true;
1771             else if (is_long_switch(*arg, "logevents"))
1772                 log_events = true;
1773             else if (is_switch(*arg, "a", "alpha"))
1774                 YXApplication::alphaBlending = true;
1775             else if (GetArgument(value, "d", "display", arg, argv+argc))
1776                 displayName = value;
1777             else if (GetArgument(value, "s", "splash", arg, argv+argc))
1778                 splashFile = value;
1779             else if (GetLongArgument(value, "trace", arg, argv+argc))
1780                 YTrace::tracing(value);
1781             else
1782                 warn(_("Unrecognized option '%s'."), *arg);
1783         }
1784     }
1785 
1786     if (restart_wm)
1787         return restartWM(displayName, overrideTheme);
1788     if (rewrite_prefs)
1789         return WMConfig::rewritePrefs(wmapp_preferences, configFile);
1790 
1791     if (isEmpty(configFile))
1792         configFile = "preferences";
1793     loadStartup(configFile);
1794 
1795     if (nonempty(overrideTheme)) {
1796         unsigned last = ACOUNT(wmapp_preferences) - 2;
1797         YConfig::freeConfig(wmapp_preferences + last);
1798         themeName = newstr(overrideTheme);
1799     }
1800     loggingEvents |= log_events;
1801     if (loggingEvents)
1802         initLogEvents();
1803 
1804     YWMApp app(&argc, &argv, displayName,
1805                 notify_parent, splashFile,
1806                 configFile, overrideTheme);
1807 
1808     return app.mainLoop();
1809 }
1810 
createTaskBar()1811 void YWMApp::createTaskBar() {
1812     if (showTaskBar && taskBar == nullptr) {
1813         taskBar = new TaskBar(this, desktop, this, this);
1814         for (YFrameIter frame = manager->focusedIterator(); ++frame; ) {
1815             frame->updateAppStatus();
1816         }
1817     }
1818 }
1819 
doLogout(RebootShutdown reboot)1820 void YWMApp::doLogout(RebootShutdown reboot) {
1821     rebootOrShutdown = reboot;
1822     if (!confirmLogout)
1823         logout();
1824     else {
1825         if (fLogoutMsgBox) {
1826             fLogoutMsgBox->unmanage();
1827         }
1828         fLogoutMsgBox = new YMsgBox(YMsgBox::mbBoth,
1829                             _("Confirm Logout"),
1830                             _("Logout will close all active applications.\n"
1831                               "Proceed?"), this, "logout");
1832     }
1833 }
1834 
logout()1835 void YWMApp::logout() {
1836     if (logoutMenu) {
1837         logoutMenu->checkCommand(actionLogout, true);
1838         logoutMenu->disableCommand(actionLogout);
1839         logoutMenu->enableCommand(actionCancelLogout);
1840     }
1841     if (logoutCommand && logoutCommand[0]) {
1842         runCommand(logoutCommand);
1843 #ifdef CONFIG_SESSION
1844     } else if (haveSessionManager()) {
1845         smRequestShutdown();
1846 #endif
1847     } else {
1848         manager->wmCloseSession();
1849         // should we always do this??
1850         manager->exitAfterLastClient(true);
1851     }
1852 }
1853 
cancelLogout()1854 void YWMApp::cancelLogout() {
1855     rebootOrShutdown = Logout;
1856     if (logoutMenu) {
1857         logoutMenu->checkCommand(actionLogout, false);
1858         logoutMenu->enableCommand(actionLogout);
1859         logoutMenu->disableCommand(actionCancelLogout);
1860     }
1861     if (logoutCancelCommand && logoutCancelCommand[0]) {
1862         runCommand(logoutCancelCommand);
1863 #ifdef CONFIG_SESSION
1864     } else if (haveSessionManager()) { // !!! this doesn't work
1865         smCancelShutdown();
1866 #endif
1867     } else {
1868         // should we always do this??
1869         manager->exitAfterLastClient(false);
1870     }
1871 }
1872 
handleMsgBox(YMsgBox * msgbox,int operation)1873 void YWMApp::handleMsgBox(YMsgBox *msgbox, int operation) {
1874     if (msgbox == fLogoutMsgBox) {
1875         msgbox->unmanage();
1876         fLogoutMsgBox = nullptr;
1877         if (operation == YMsgBox::mbOK) {
1878             logout();
1879         }
1880     }
1881     if (msgbox == fRestartMsgBox) {
1882         msgbox->unmanage();
1883         fRestartMsgBox = nullptr;
1884         if (operation == YMsgBox::mbOK) {
1885             restartClient(TERM, nullptr);
1886         }
1887     }
1888 }
1889 
handleSMAction(WMAction message)1890 void YWMApp::handleSMAction(WMAction message) {
1891     static const pair<WMAction, EAction> pairs[] = {
1892         { ICEWM_ACTION_LOGOUT,        actionLogout },
1893         { ICEWM_ACTION_CANCEL_LOGOUT, actionCancelLogout },
1894         { ICEWM_ACTION_REBOOT,        actionReboot },
1895         { ICEWM_ACTION_SHUTDOWN,      actionShutdown },
1896         { ICEWM_ACTION_ABOUT,         actionAbout },
1897         { ICEWM_ACTION_WINDOWLIST,    actionWindowList },
1898         { ICEWM_ACTION_RESTARTWM,     actionRestart },
1899         { ICEWM_ACTION_SUSPEND,       actionSuspend },
1900         { ICEWM_ACTION_WINOPTIONS,    actionWinOptions },
1901         { ICEWM_ACTION_RELOADKEYS,    actionReloadKeys },
1902     };
1903     for (auto p : pairs)
1904         if (message == p.left)
1905             return actionPerformed(p.right);
1906 }
1907 
refreshDesktop()1908 void YWMApp::refreshDesktop() {
1909     YWindow* w = new YWindow;
1910     Window handle = None;
1911     long mask = 0xFFFFFF;
1912 
1913     for (int i = 0; i <= 2 && w; ++i) {
1914         if (i == 0) {
1915             w->setStyle(YWindow::wsOverrideRedirect | YWindow::wsDesktopAware);
1916             w->setGeometry(desktop->geometry());
1917             handle = w->handle();
1918             XSelectInput(display(), handle, mask);
1919             w->raise();
1920             w->show();
1921         }
1922         else if (i == 1) {
1923             w->setTitle("IceRefresh");
1924         }
1925         else if (i == 2) {
1926             delete w; w = nullptr;
1927         }
1928         sync();
1929         XEvent event;
1930         while (XCheckWindowEvent(display(), handle, mask, &event)) {
1931         }
1932     }
1933 }
1934 
1935 class SplashWindow : public YWindow {
1936     ref<YImage> image;
1937 public:
SplashWindow(ref<YImage> image,int depth,Visual * visual)1938     SplashWindow(ref<YImage> image, int depth, Visual* visual) :
1939         YWindow(nullptr, None, depth, visual),
1940         image(image)
1941     {
1942         setToplevel(true);
1943         setStyle(wsOverrideRedirect | wsSaveUnder | wsNoExpose);
1944         place();
1945         XSelectInput(xapp->display(), handle(), VisibilityChangeMask);
1946         props();
1947         show();
1948         repaint();
1949         xapp->sync();
1950     }
place()1951     void place() {
1952         YRect geo(desktop->getScreenGeometry());
1953         int w = int(image->width());
1954         int h = int(image->height());
1955         int x = geo.x() + (geo.width() - w) / 2;
1956         int y = geo.y() + (geo.height() - h) / 2;
1957         setGeometry(YRect(x, y, w, h));
1958     }
props()1959     void props() {
1960         setTitle("IceSplash");
1961         setClassHint("splash", "IceWM");
1962         setNetOpacity(82 * (0xFFFFFFFF / 100));
1963         setNetWindowType(_XA_NET_WM_WINDOW_TYPE_SPLASH);
1964         setProperty(_XA_WIN_LAYER, XA_CARDINAL, 15);
1965     }
repaint()1966     void repaint() {
1967         GraphicsBuffer(this).paint();
1968     }
handleExpose(const XExposeEvent &)1969     void handleExpose(const XExposeEvent&) {
1970     }
paint(Graphics & g,const YRect &)1971     void paint(Graphics& g, const YRect&) {
1972         g.copyImage(image, 0, 0);
1973     }
handleVisibility(const XVisibilityEvent &)1974     void handleVisibility(const XVisibilityEvent&) {
1975         raise();
1976         xapp->sync();
1977     }
1978 };
1979 
splash(const char * splashFile)1980 YWindow* YWMApp::splash(const char* splashFile) {
1981     YWindow* window(nullptr);
1982     if (splashFile && 4 < strlen(splashFile) && !post_preferences) {
1983         upath path(findConfigFile(splashFile));
1984         if (path.nonempty()) {
1985             ref<YImage> imag(YImage::load(path));
1986             if (imag != null) {
1987                 unsigned depth = DefaultDepth(display(), screen());
1988                 Visual* visual = DefaultVisual(display(), screen());
1989                 window = new SplashWindow(imag, depth, visual);
1990                 window->unmanageWindow();
1991                 window->raise();
1992                 splashTimer->setTimer(1000L, this, true);
1993             }
1994         }
1995     }
1996     return window;
1997 }
1998 
GuiSignaler()1999 GuiSignaler::GuiSignaler() :
2000     started(false),
2001     next(zerotime())
2002 {
2003 }
2004 
name(GUIEvent e)2005 const char* GuiSignaler::name(GUIEvent e) {
2006     return gui_event_names[e];
2007 }
2008 
signal(GUIEvent ge)2009 void GuiSignaler::signal(GUIEvent ge) {
2010     /*
2011      * The first event must be geStartup.
2012      * Ignore all other events before that.
2013      */
2014     if (started == false) {
2015         if (ge == geStartup) {
2016             started = true;
2017         } else {
2018             // tlog("%s: not started for %s", __func__, name(ge));
2019             return;
2020         }
2021     }
2022 
2023     /*
2024      * Because there is no event buffering,
2025      * when multiple events occur in a burst,
2026      * only signal the first event of the burst.
2027      */
2028     timeval now = monotime();
2029     if (now < next && ge != geStartup) {
2030         // tlog("%s: ignoring %s", __func__, name(ge));
2031         return;
2032     }
2033 
2034     // tlog("%s: signaling %s", __func__, name(ge));
2035     next = now + millitime(ge == geStartup ? 1000L : 100L);
2036 
2037     unsigned char num = static_cast<unsigned char>(ge);
2038 
2039     XChangeProperty(xapp->display(), desktop->handle(),
2040                     _XA_ICEWM_GUIEVENT, _XA_ICEWM_GUIEVENT,
2041                     8, PropModeReplace, &num, 1);
2042 }
2043 
2044 // vim: set sw=4 ts=4 et:
2045