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