wmaker: replace and be replaced (ICCCM protocol)
[wmaker-crm.git] / src / screen.c
blob1f246c45e40455066adbb1cfed088aeab4b5badb
1 /* screen.c - screen management
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "wconfig.h"
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
28 #include <X11/Xlib.h>
29 #include <X11/Xutil.h>
30 #include <X11/Xatom.h>
31 #ifdef KEEP_XKB_LOCK_STATUS
32 #include <X11/XKBlib.h>
33 #endif /* KEEP_XKB_LOCK_STATUS */
34 #ifdef USE_RANDR
35 #include <X11/extensions/Xrandr.h>
36 #endif
38 #include <wraster.h>
39 #include "WindowMaker.h"
40 #include "def_pixmaps.h"
41 #include "screen.h"
42 #include "texture.h"
43 #include "pixmap.h"
44 #include "menu.h"
45 #include "window.h"
46 #include "main.h"
47 #include "actions.h"
48 #include "properties.h"
49 #include "dock.h"
50 #include "resources.h"
51 #include "workspace.h"
52 #include "session.h"
53 #include "balloon.h"
54 #include "geomview.h"
55 #include "wmspec.h"
56 #include "rootmenu.h"
58 #include "xinerama.h"
60 #include <WINGs/WUtil.h>
62 #include "defaults.h"
64 #define EVENT_MASK (LeaveWindowMask|EnterWindowMask|PropertyChangeMask\
65 |SubstructureNotifyMask|PointerMotionMask \
66 |SubstructureRedirectMask|ButtonPressMask|ButtonReleaseMask\
67 |KeyPressMask|KeyReleaseMask)
69 #define REPLACE_WM_TIMEOUT 15
71 #define STIPPLE_WIDTH 2
72 #define STIPPLE_HEIGHT 2
73 static char STIPPLE_DATA[] = { 0x02, 0x01 };
75 static int CantManageScreen = 0;
77 static WMPropList *dApplications = NULL;
78 static WMPropList *dWorkspace;
79 static WMPropList *dDock;
80 static WMPropList *dClip;
81 static WMPropList *dDrawers = NULL;
83 static void make_keys(void)
85 if (dApplications != NULL)
86 return;
88 dApplications = WMCreatePLString("Applications");
89 dWorkspace = WMCreatePLString("Workspace");
90 dDock = WMCreatePLString("Dock");
91 dClip = WMCreatePLString("Clip");
92 dDrawers = WMCreatePLString("Drawers");
96 * Support for ICCCM 2.0: Window Manager Replacement protocol
97 * See: http://www.x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html
99 * Basically, user should be able to dynamically change its window manager; this is done
100 * cooperatively through a special Selection ("WM_Sn" where 'n' is the X screen number)
102 * This function does 2 things:
103 * - it checks if this selection is already owned, which means that another window
104 * manager is running. If it is the case and user specified '--replace' on the command
105 * line, then it asks the WM to shut down;
106 * - when ok, it sets the selection ownership to ourself, so another window manager
107 * may ask us to shut down (this is handled in "event.c")
109 static Bool replace_existing_wm(WScreen *scr)
111 char atomName[16];
112 Window wm;
113 XSetWindowAttributes attribs;
114 XClientMessageEvent event;
115 unsigned long current_time;
116 int ret;
118 /* Try to acquire the atom named WM_S<screen> */
119 ret = snprintf(atomName, sizeof(atomName), "WM_S%d", scr->screen);
120 if (ret < 0 || ret == sizeof(atomName)) {
121 werror("out of memory trying to allocate window manager selection atom for screen %d", scr->screen);
122 return False;
125 scr->sn_atom = XInternAtom(dpy, atomName, False);
126 if (! scr->sn_atom)
127 return False;
129 /* Check if an existing window manager owns the selection */
130 wm = XGetSelectionOwner(dpy, scr->sn_atom);
131 if (wm) {
132 if (!wPreferences.flags.replace) {
133 wmessage(_("another window manager is running"));
134 wwarning(_("use the --replace flag to replace it"));
135 return False;
138 attribs.event_mask = StructureNotifyMask;
139 if (!XChangeWindowAttributes(dpy, wm, CWEventMask, &attribs))
140 wm = None;
143 /* for our window manager info notice board and the selection owner */
144 scr->info_window = XCreateSimpleWindow(dpy, scr->root_win, 0, 0, 10, 10, 0, 0, 0);
146 /* Try to acquire the selection */
147 current_time = CurrentTime;
148 ret = XSetSelectionOwner(dpy, scr->sn_atom, scr->info_window, current_time);
149 if (ret == BadAtom || ret == BadWindow)
150 return False;
152 /* Wait for other window manager to exit */
153 if (wm) {
154 unsigned long wait = 0;
155 unsigned long timeout = REPLACE_WM_TIMEOUT * 1000000L;
156 XEvent event;
158 while (wait < timeout) {
159 if (!(wait % 1000000))
160 wmessage(_("waiting %lus for other window manager to exit"), (timeout - wait) / 1000000L);
162 if (XCheckWindowEvent(dpy, wm, StructureNotifyMask, &event))
163 if (event.type == DestroyNotify)
164 break;
166 wusleep(100000);
167 wait += 100000;
170 if (wait >= timeout) {
171 wwarning(_("other window manager hasn't exited!"));
172 return False;
175 wmessage(_("replacing the other window manager"));
178 if (XGetSelectionOwner(dpy, scr->sn_atom) != scr->info_window)
179 return False;
181 event.type = ClientMessage;
182 event.message_type = scr->sn_atom;
183 event.format = 32;
184 event.data.l[0] = (long) current_time;
185 event.data.l[1] = (long) scr->sn_atom;
186 event.data.l[2] = (long) scr->info_window;
187 event.data.l[3] = (long) 0L;
188 event.data.l[4] = (long) 0L;
189 event.window = scr->root_win;
190 XSendEvent(dpy, scr->root_win, False, StructureNotifyMask, (XEvent *) &event);
192 return True;
196 *----------------------------------------------------------------------
197 * alreadyRunningError--
198 * X error handler used to catch errors when trying to do
199 * XSelectInput() on the root window. These errors probably mean that
200 * there already is some other window manager running.
202 * Returns:
203 * Nothing, unless something really evil happens...
205 * Side effects:
206 * CantManageScreen is set to 1;
207 *----------------------------------------------------------------------
209 static int alreadyRunningError(Display * dpy, XErrorEvent * error)
211 /* Parameter not used, but tell the compiler that it is ok */
212 (void) dpy;
213 (void) error;
215 CantManageScreen = 1;
216 return -1;
220 *----------------------------------------------------------------------
221 * allocButtonPixmaps--
222 * Allocate pixmaps used on window operation buttons (those in the
223 * titlebar). The pixmaps are linked to the program. If XPM is supported
224 * XPM pixmaps are used otherwise, equivalent bitmaps are used.
226 * Returns:
227 * Nothing
229 * Side effects:
230 * Allocates shared pixmaps for the screen. These pixmaps should
231 * not be freed by anybody.
232 *----------------------------------------------------------------------
234 static void allocButtonPixmaps(WScreen * scr)
236 WPixmap *pix;
238 /* create predefined pixmaps */
239 if (wPreferences.new_style == TS_NEXT) {
240 pix = wPixmapCreateFromXPMData(scr, NEXT_CLOSE_XPM);
241 } else {
242 pix = wPixmapCreateFromXPMData(scr, PRED_CLOSE_XPM);
244 if (pix)
245 pix->shared = 1;
246 scr->b_pixmaps[WBUT_CLOSE] = pix;
248 if (wPreferences.new_style == TS_NEXT) {
249 pix = wPixmapCreateFromXPMData(scr, NEXT_BROKEN_CLOSE_XPM);
250 } else {
251 pix = wPixmapCreateFromXPMData(scr, PRED_BROKEN_CLOSE_XPM);
253 if (pix)
254 pix->shared = 1;
255 scr->b_pixmaps[WBUT_BROKENCLOSE] = pix;
257 if (wPreferences.new_style == TS_NEXT) {
258 pix = wPixmapCreateFromXPMData(scr, NEXT_ICONIFY_XPM);
259 } else {
260 pix = wPixmapCreateFromXPMData(scr, PRED_ICONIFY_XPM);
262 if (pix)
263 pix->shared = 1;
264 scr->b_pixmaps[WBUT_ICONIFY] = pix;
265 #ifdef XKB_BUTTON_HINT
266 if (wPreferences.new_style == TS_NEXT) {
267 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP1_XPM);
268 } else {
269 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP1_XPM);
271 if (pix)
272 pix->shared = 1;
273 scr->b_pixmaps[WBUT_XKBGROUP1] = pix;
274 if (wPreferences.new_style == TS_NEXT) {
275 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP2_XPM);
276 } else {
277 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP2_XPM);
279 if (pix)
280 pix->shared = 1;
281 scr->b_pixmaps[WBUT_XKBGROUP2] = pix;
282 if (wPreferences.new_style == TS_NEXT) {
283 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP3_XPM);
284 } else {
285 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP3_XPM);
287 if (pix)
288 pix->shared = 1;
289 scr->b_pixmaps[WBUT_XKBGROUP3] = pix;
290 if (wPreferences.new_style == TS_NEXT) {
291 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP4_XPM);
292 } else {
293 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP4_XPM);
295 if (pix)
296 pix->shared = 1;
297 scr->b_pixmaps[WBUT_XKBGROUP4] = pix;
298 #endif
300 if (wPreferences.new_style == TS_NEXT) {
301 pix = wPixmapCreateFromXPMData(scr, NEXT_KILL_XPM);
302 } else {
303 pix = wPixmapCreateFromXPMData(scr, PRED_KILL_XPM);
305 if (pix)
306 pix->shared = 1;
307 scr->b_pixmaps[WBUT_KILL] = pix;
310 static void draw_dot(WScreen * scr, Drawable d, int x, int y, GC gc)
312 XSetForeground(dpy, gc, scr->black_pixel);
313 XDrawLine(dpy, d, gc, x, y, x + 1, y);
314 XDrawPoint(dpy, d, gc, x, y + 1);
315 XSetForeground(dpy, gc, scr->white_pixel);
316 XDrawLine(dpy, d, gc, x + 2, y, x + 2, y + 1);
317 XDrawPoint(dpy, d, gc, x + 1, y + 1);
320 static WPixmap *make3Dots(WScreen * scr)
322 WPixmap *wpix;
323 GC gc2, gc;
324 XGCValues gcv;
325 Pixmap pix, mask;
327 gc = scr->copy_gc;
328 pix = XCreatePixmap(dpy, scr->w_win, wPreferences.icon_size, wPreferences.icon_size, scr->w_depth);
329 XSetForeground(dpy, gc, scr->black_pixel);
330 XFillRectangle(dpy, pix, gc, 0, 0, wPreferences.icon_size, wPreferences.icon_size);
331 XSetForeground(dpy, gc, scr->white_pixel);
332 draw_dot(scr, pix, 4, wPreferences.icon_size - 6, gc);
333 draw_dot(scr, pix, 9, wPreferences.icon_size - 6, gc);
334 draw_dot(scr, pix, 14, wPreferences.icon_size - 6, gc);
336 mask = XCreatePixmap(dpy, scr->w_win, wPreferences.icon_size, wPreferences.icon_size, 1);
337 gcv.foreground = 0;
338 gcv.graphics_exposures = False;
339 gc2 = XCreateGC(dpy, mask, GCForeground | GCGraphicsExposures, &gcv);
340 XFillRectangle(dpy, mask, gc2, 0, 0, wPreferences.icon_size, wPreferences.icon_size);
341 XSetForeground(dpy, gc2, 1);
342 XFillRectangle(dpy, mask, gc2, 4, wPreferences.icon_size - 6, 3, 2);
343 XFillRectangle(dpy, mask, gc2, 9, wPreferences.icon_size - 6, 3, 2);
344 XFillRectangle(dpy, mask, gc2, 14, wPreferences.icon_size - 6, 3, 2);
346 XFreeGC(dpy, gc2);
348 wpix = wPixmapCreate(pix, mask);
349 wpix->shared = 1;
351 return wpix;
354 static void allocGCs(WScreen * scr)
356 XGCValues gcv;
357 XColor color;
358 int gcm;
360 scr->stipple_bitmap = XCreateBitmapFromData(dpy, scr->w_win, STIPPLE_DATA, STIPPLE_WIDTH, STIPPLE_HEIGHT);
362 gcv.stipple = scr->stipple_bitmap;
363 gcv.foreground = scr->white_pixel;
364 gcv.fill_style = FillStippled;
365 gcv.graphics_exposures = False;
366 gcm = GCForeground | GCStipple | GCFillStyle | GCGraphicsExposures;
367 scr->stipple_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
369 /* selected icon border GCs */
370 gcv.function = GXcopy;
371 gcv.foreground = scr->white_pixel;
372 gcv.background = scr->black_pixel;
373 gcv.line_width = 1;
374 gcv.line_style = LineDoubleDash;
375 gcv.fill_style = FillSolid;
376 gcv.dash_offset = 0;
377 gcv.dashes = 4;
378 gcv.graphics_exposures = False;
380 gcm = GCFunction | GCGraphicsExposures;
381 gcm |= GCForeground | GCBackground;
382 gcm |= GCLineWidth | GCLineStyle;
383 gcm |= GCFillStyle;
384 gcm |= GCDashOffset | GCDashList;
386 scr->icon_select_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
388 scr->menu_title_color[0] = WMRetainColor(scr->white);
390 /* don't retain scr->black here because we may alter its alpha */
391 scr->mtext_color = WMCreateRGBColor(scr->wmscreen, 0, 0, 0, True);
392 scr->dtext_color = WMCreateRGBColor(scr->wmscreen, 0, 0, 0, True);
394 /* frame GC */
395 wGetColor(scr, DEF_FRAME_COLOR, &color);
396 gcv.function = GXxor;
397 /* this will raise the probability of the XORed color being different
398 * of the original color in PseudoColor when not all color cells are
399 * initialized */
400 if (DefaultVisual(dpy, scr->screen)->class == PseudoColor)
401 gcv.plane_mask = (1 << (scr->depth - 1)) | 1;
402 else
403 gcv.plane_mask = AllPlanes;
404 gcv.foreground = color.pixel;
405 if (gcv.foreground == 0)
406 gcv.foreground = 1;
407 gcv.line_width = DEF_FRAME_THICKNESS;
408 gcv.subwindow_mode = IncludeInferiors;
409 gcv.graphics_exposures = False;
410 scr->frame_gc = XCreateGC(dpy, scr->root_win, GCForeground | GCGraphicsExposures
411 | GCFunction | GCSubwindowMode | GCLineWidth | GCPlaneMask, &gcv);
413 /* line GC */
414 gcv.foreground = color.pixel;
416 if (gcv.foreground == 0)
417 /* XOR:ing with a zero is not going to be of much use, so
418 in that case, we somewhat arbitrarily xor with 17 instead. */
419 gcv.foreground = 17;
421 gcv.function = GXxor;
422 gcv.subwindow_mode = IncludeInferiors;
423 gcv.line_width = 1;
424 gcv.cap_style = CapRound;
425 gcv.graphics_exposures = False;
426 gcm = GCForeground | GCFunction | GCSubwindowMode | GCLineWidth | GCCapStyle | GCGraphicsExposures;
427 scr->line_gc = XCreateGC(dpy, scr->root_win, gcm, &gcv);
429 scr->line_pixel = gcv.foreground;
431 /* copy GC */
432 gcv.foreground = scr->white_pixel;
433 gcv.background = scr->black_pixel;
434 gcv.graphics_exposures = False;
435 scr->copy_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
437 /* misc drawing GC */
438 gcv.graphics_exposures = False;
439 gcm = GCGraphicsExposures;
440 scr->draw_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
442 assert(scr->stipple_bitmap != None);
444 /* mono GC */
445 scr->mono_gc = XCreateGC(dpy, scr->stipple_bitmap, gcm, &gcv);
448 static void createPixmaps(WScreen * scr)
450 WPixmap *pix;
452 /* load pixmaps */
453 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_RADIO_INDICATOR_XBM_DATA,
454 (char *)MENU_RADIO_INDICATOR_XBM_DATA,
455 MENU_RADIO_INDICATOR_XBM_SIZE,
456 MENU_RADIO_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
457 if (pix != NULL)
458 pix->shared = 1;
459 scr->menu_radio_indicator = pix;
461 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_CHECK_INDICATOR_XBM_DATA,
462 (char *)MENU_CHECK_INDICATOR_XBM_DATA,
463 MENU_CHECK_INDICATOR_XBM_SIZE,
464 MENU_CHECK_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
465 if (pix != NULL)
466 pix->shared = 1;
467 scr->menu_check_indicator = pix;
469 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_MINI_INDICATOR_XBM_DATA,
470 (char *)MENU_MINI_INDICATOR_XBM_DATA,
471 MENU_MINI_INDICATOR_XBM_SIZE,
472 MENU_MINI_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
473 if (pix != NULL)
474 pix->shared = 1;
475 scr->menu_mini_indicator = pix;
477 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_HIDE_INDICATOR_XBM_DATA,
478 (char *)MENU_HIDE_INDICATOR_XBM_DATA,
479 MENU_HIDE_INDICATOR_XBM_SIZE,
480 MENU_HIDE_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
481 if (pix != NULL)
482 pix->shared = 1;
483 scr->menu_hide_indicator = pix;
485 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_SHADE_INDICATOR_XBM_DATA,
486 (char *)MENU_SHADE_INDICATOR_XBM_DATA,
487 MENU_SHADE_INDICATOR_XBM_SIZE,
488 MENU_SHADE_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
489 if (pix != NULL)
490 pix->shared = 1;
491 scr->menu_shade_indicator = pix;
493 create_logo_image(scr);
495 scr->dock_dots = make3Dots(scr);
497 /* titlebar button pixmaps */
498 allocButtonPixmaps(scr);
501 void create_logo_image(WScreen *scr)
503 RImage *image = get_icon_image(scr, "Logo", "WMPanel", 128);
505 if (!image) {
506 wwarning(_("could not load logo image for panels: %s"), RMessageForError(RErrorCode));
507 } else {
508 WMSetApplicationIconImage(scr->wmscreen, image);
509 RReleaseImage(image);
514 *----------------------------------------------------------------------
515 * createInternalWindows--
516 * Creates some windows used internally by the program. One to
517 * receive input focus when no other window can get it and another
518 * to display window geometry information during window resize/move.
520 * Returns:
521 * Nothing
523 * Side effects:
524 * Windows are created and some colors are allocated for the
525 * window background.
526 *----------------------------------------------------------------------
528 static void createInternalWindows(WScreen * scr)
530 int vmask;
531 XSetWindowAttributes attribs;
533 /* InputOnly window to get the focus when no other window can get it */
534 vmask = CWEventMask | CWOverrideRedirect;
535 attribs.event_mask = KeyPressMask | FocusChangeMask;
536 attribs.override_redirect = True;
537 scr->no_focus_win = XCreateWindow(dpy, scr->root_win, -10, -10, 4, 4, 0, 0,
538 InputOnly, CopyFromParent, vmask, &attribs);
539 XSelectInput(dpy, scr->no_focus_win, KeyPressMask | KeyReleaseMask);
540 XMapWindow(dpy, scr->no_focus_win);
542 XSetInputFocus(dpy, scr->no_focus_win, RevertToParent, CurrentTime);
544 /* shadow window for dock buttons */
545 vmask = CWBorderPixel | CWBackPixmap | CWBackPixel | CWCursor | CWSaveUnder | CWOverrideRedirect;
546 attribs.border_pixel = scr->black_pixel;
547 attribs.save_under = True;
548 attribs.override_redirect = True;
549 attribs.background_pixmap = None;
550 attribs.background_pixel = scr->white_pixel;
551 attribs.cursor = wPreferences.cursor[WCUR_NORMAL];
552 vmask |= CWColormap;
553 attribs.colormap = scr->w_colormap;
554 scr->dock_shadow =
555 XCreateWindow(dpy, scr->root_win, 0, 0, wPreferences.icon_size,
556 wPreferences.icon_size, 0, scr->w_depth, CopyFromParent, scr->w_visual, vmask, &attribs);
558 /* workspace name */
559 vmask = CWBackPixel | CWSaveUnder | CWOverrideRedirect | CWColormap | CWBorderPixel;
560 attribs.save_under = True;
561 attribs.override_redirect = True;
562 attribs.colormap = scr->w_colormap;
563 attribs.background_pixel = scr->icon_back_texture->normal.pixel;
564 attribs.border_pixel = 0; /* do not care */
565 scr->workspace_name =
566 XCreateWindow(dpy, scr->root_win, 0, 0, 10, 10, 0, scr->w_depth,
567 CopyFromParent, scr->w_visual, vmask, &attribs);
571 *----------------------------------------------------------------------
572 * wScreenInit--
573 * Initializes the window manager for the given screen and
574 * allocates a WScreen descriptor for it. Many resources are allocated
575 * for the screen and the root window is setup appropriately.
577 * Returns:
578 * The WScreen descriptor for the screen.
580 * Side effects:
581 * Many resources are allocated and the IconSize property is
582 * set on the root window.
583 * The program can be aborted if some fatal error occurs.
585 * TODO: User specifiable visual.
586 *----------------------------------------------------------------------
588 WScreen *wScreenInit(int screen_number)
590 WScreen *scr;
591 XIconSize icon_size[1];
592 RContextAttributes rattr;
593 long event_mask;
594 XErrorHandler oldHandler;
595 int i;
597 scr = wmalloc(sizeof(WScreen));
599 scr->stacking_list = WMCreateTreeBag();
601 /* initialize globals */
602 scr->screen = screen_number;
603 scr->root_win = RootWindow(dpy, screen_number);
604 scr->depth = DefaultDepth(dpy, screen_number);
605 scr->colormap = DefaultColormap(dpy, screen_number);
607 scr->scr_width = WidthOfScreen(ScreenOfDisplay(dpy, screen_number));
608 scr->scr_height = HeightOfScreen(ScreenOfDisplay(dpy, screen_number));
610 wInitXinerama(scr);
612 scr->usableArea = (WArea *) wmalloc(sizeof(WArea) * wXineramaHeads(scr));
613 scr->totalUsableArea = (WArea *) wmalloc(sizeof(WArea) * wXineramaHeads(scr));
615 for (i = 0; i < wXineramaHeads(scr); ++i) {
616 WMRect rect = wGetRectForHead(scr, i);
617 scr->usableArea[i].x1 = scr->totalUsableArea[i].x1 = rect.pos.x;
618 scr->usableArea[i].y1 = scr->totalUsableArea[i].y1 = rect.pos.y;
619 scr->usableArea[i].x2 = scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width;
620 scr->usableArea[i].y2 = scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
623 scr->fakeGroupLeaders = WMCreateArray(16);
625 CantManageScreen = 0;
626 oldHandler = XSetErrorHandler(alreadyRunningError);
628 /* Do we need to replace an existing window manager? */
629 if (!replace_existing_wm(scr)) {
630 XDestroyWindow(dpy, scr->info_window);
631 wfree(scr);
632 return NULL;
635 event_mask = EVENT_MASK;
636 XSelectInput(dpy, scr->root_win, event_mask);
638 #ifdef KEEP_XKB_LOCK_STATUS
639 /* Only GroupLock doesn't work correctly in my system since right-alt
640 * can change mode while holding it too - ]d
642 if (w_global.xext.xkb.supported) {
643 XkbSelectEvents(dpy, XkbUseCoreKbd, XkbStateNotifyMask, XkbStateNotifyMask);
645 #endif /* KEEP_XKB_LOCK_STATUS */
647 #ifdef USE_RANDR
648 if (w_global.xext.randr.supported)
649 XRRSelectInput(dpy, scr->root_win, RRScreenChangeNotifyMask);
650 #endif
652 XSync(dpy, False);
653 XSetErrorHandler(oldHandler);
655 if (CantManageScreen) {
656 wfree(scr);
657 return NULL;
660 XDefineCursor(dpy, scr->root_win, wPreferences.cursor[WCUR_ROOT]);
662 /* screen descriptor for raster graphic library */
663 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap;
664 rattr.render_mode = wPreferences.no_dithering ? RBestMatchRendering : RDitheredRendering;
666 /* if the std colormap stuff works ok, this will be ignored */
667 rattr.colors_per_channel = wPreferences.cmap_size;
668 if (rattr.colors_per_channel < 2)
669 rattr.colors_per_channel = 2;
671 /* Use standard colormap */
672 rattr.standard_colormap_mode = RUseStdColormap;
674 if (getWVisualID(screen_number) >= 0) {
675 rattr.flags |= RC_VisualID;
676 rattr.visualid = getWVisualID(screen_number);
679 scr->rcontext = RCreateContext(dpy, screen_number, &rattr);
681 if (!scr->rcontext && RErrorCode == RERR_STDCMAPFAIL) {
682 wwarning("%s", RMessageForError(RErrorCode));
684 rattr.flags &= ~RC_StandardColormap;
685 rattr.standard_colormap_mode = RUseStdColormap;
687 scr->rcontext = RCreateContext(dpy, screen_number, &rattr);
689 if (scr->rcontext == NULL) {
690 wfatal(_("can't create Context on screen %d, %s"),
691 screen_number, RMessageForError(RErrorCode));
692 goto abort_no_context;
695 scr->w_win = scr->rcontext->drawable;
696 scr->w_visual = scr->rcontext->visual;
697 scr->w_depth = scr->rcontext->depth;
698 scr->w_colormap = scr->rcontext->cmap;
700 /* create screen descriptor for WINGs */
701 scr->wmscreen = WMCreateScreenWithRContext(dpy, screen_number, scr->rcontext);
703 if (!scr->wmscreen) {
704 wfatal(_("could not initialize WINGs widget set"));
705 RDestroyContext(scr->rcontext);
706 abort_no_context:
707 WMFreeArray(scr->fakeGroupLeaders);
708 wfree(scr->totalUsableArea);
709 wfree(scr->usableArea);
710 WMFreeBag(scr->stacking_list);
711 wfree(scr);
712 return NULL;
715 scr->black = WMBlackColor(scr->wmscreen);
716 scr->white = WMWhiteColor(scr->wmscreen);
717 scr->gray = WMGrayColor(scr->wmscreen);
718 scr->darkGray = WMDarkGrayColor(scr->wmscreen);
720 scr->black_pixel = WMColorPixel(scr->black); /*scr->rcontext->black; */
721 scr->white_pixel = WMColorPixel(scr->white); /*scr->rcontext->white; */
722 scr->light_pixel = WMColorPixel(scr->gray);
723 scr->dark_pixel = WMColorPixel(scr->darkGray);
725 /* create GCs with default values */
726 allocGCs(scr);
728 /* read defaults for this screen */
729 wReadDefaults(scr, w_global.domain.wmaker->dictionary);
732 XColor xcol;
733 /* frame boder color */
734 wGetColor(scr, WMGetColorRGBDescription(scr->frame_border_color), &xcol);
735 scr->frame_border_pixel = xcol.pixel;
736 wGetColor(scr, WMGetColorRGBDescription(scr->frame_focused_border_color), &xcol);
737 scr->frame_focused_border_pixel = xcol.pixel;
738 wGetColor(scr, WMGetColorRGBDescription(scr->frame_selected_border_color), &xcol);
739 scr->frame_selected_border_pixel = xcol.pixel;
742 createInternalWindows(scr);
744 wNETWMInitStuff(scr);
746 /* create initial workspace */
747 wWorkspaceNew(scr);
749 /* create shared pixmaps */
750 createPixmaps(scr);
752 /* set icon sizes we can accept from clients */
753 icon_size[0].min_width = 8;
754 icon_size[0].min_height = 8;
755 icon_size[0].max_width = wPreferences.icon_size - 4;
756 icon_size[0].max_height = wPreferences.icon_size - 4;
757 icon_size[0].width_inc = 1;
758 icon_size[0].height_inc = 1;
759 XSetIconSizes(dpy, scr->root_win, icon_size, 1);
761 /* setup WindowMaker protocols property in the root window */
762 PropSetWMakerProtocols(scr->root_win);
764 /* setup our noticeboard */
765 XChangeProperty(dpy, scr->info_window, w_global.atom.wmaker.noticeboard,
766 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&scr->info_window, 1);
767 XChangeProperty(dpy, scr->root_win, w_global.atom.wmaker.noticeboard,
768 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&scr->info_window, 1);
770 #ifdef BALLOON_TEXT
771 /* initialize balloon text stuff */
772 wBalloonInitialize(scr);
773 #endif
775 scr->info_text_font = WMBoldSystemFontOfSize(scr->wmscreen, 12);
777 scr->tech_draw_font = XLoadQueryFont(dpy, "-adobe-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*");
778 if (!scr->tech_draw_font)
779 scr->tech_draw_font = XLoadQueryFont(dpy, "fixed");
781 scr->gview = WCreateGeometryView(scr->wmscreen);
782 WMRealizeWidget(scr->gview);
784 wScreenUpdateUsableArea(scr);
786 return scr;
789 void wScreenUpdateUsableArea(WScreen *scr)
792 * scr->totalUsableArea[] will become the usableArea used for Windowplacement,
793 * scr->usableArea[] will be used for iconplacement, hence no iconyard nor
794 * border.
797 WArea area;
798 int i;
799 unsigned long tmp_area, best_area = 0;
800 unsigned int size = wPreferences.workspace_border_size;
801 unsigned int position = wPreferences.workspace_border_position;
803 for (i = 0; i < wXineramaHeads(scr); ++i) {
804 WMRect rect = wGetRectForHead(scr, i);
805 scr->totalUsableArea[i].x1 = rect.pos.x;
806 scr->totalUsableArea[i].y1 = rect.pos.y;
807 scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width;
808 scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
810 if (wNETWMGetUsableArea(scr, i, &area)) {
811 scr->totalUsableArea[i].x1 = WMAX(scr->totalUsableArea[i].x1, area.x1);
812 scr->totalUsableArea[i].y1 = WMAX(scr->totalUsableArea[i].y1, area.y1);
813 scr->totalUsableArea[i].x2 = WMIN(scr->totalUsableArea[i].x2, area.x2);
814 scr->totalUsableArea[i].y2 = WMIN(scr->totalUsableArea[i].y2, area.y2);
817 scr->usableArea[i] = scr->totalUsableArea[i];
819 if (wPreferences.no_window_over_icons) {
820 if (wPreferences.icon_yard & IY_VERT) {
821 if (wPreferences.icon_yard & IY_RIGHT)
822 scr->totalUsableArea[i].x2 -= wPreferences.icon_size;
823 else
824 scr->totalUsableArea[i].x1 += wPreferences.icon_size;
825 } else {
826 if (wPreferences.icon_yard & IY_TOP)
827 scr->totalUsableArea[i].y1 += wPreferences.icon_size;
828 else
829 scr->totalUsableArea[i].y2 -= wPreferences.icon_size;
833 if (scr->totalUsableArea[i].x2 - scr->totalUsableArea[i].x1 < rect.size.width / 2) {
834 scr->totalUsableArea[i].x1 = rect.pos.x;
835 scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width;
838 if (scr->totalUsableArea[i].y2 - scr->totalUsableArea[i].y1 < rect.size.height / 2) {
839 scr->totalUsableArea[i].y1 = rect.pos.y;
840 scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
843 tmp_area = (scr->totalUsableArea[i].x2 - scr->totalUsableArea[i].x1) *
844 (scr->totalUsableArea[i].y2 - scr->totalUsableArea[i].y1);
846 if (tmp_area > best_area) {
847 best_area = tmp_area;
848 area = scr->totalUsableArea[i];
851 if (size > 0 && position != WB_NONE) {
852 if (position & WB_LEFTRIGHT) {
853 scr->totalUsableArea[i].x1 += size;
854 scr->totalUsableArea[i].x2 -= size;
856 if (position & WB_TOPBOTTOM) {
857 scr->totalUsableArea[i].y1 += size;
858 scr->totalUsableArea[i].y2 -= size;
863 if (wPreferences.auto_arrange_icons)
864 wArrangeIcons(scr, True);
867 void wScreenRestoreState(WScreen * scr)
869 WMPropList *state;
870 char *path;
872 OpenRootMenu(scr, -10000, -10000, False);
873 wMenuUnmap(scr->root_menu);
875 make_keys();
877 if (w_global.screen_count == 1) {
878 path = wdefaultspathfordomain("WMState");
879 } else {
880 char buf[16];
881 snprintf(buf, sizeof(buf), "WMState.%i", scr->screen);
882 path = wdefaultspathfordomain(buf);
884 scr->session_state = WMReadPropListFromFile(path);
885 wfree(path);
886 if (!scr->session_state && w_global.screen_count > 1) {
887 path = wdefaultspathfordomain("WMState");
888 scr->session_state = WMReadPropListFromFile(path);
889 wfree(path);
892 if (!scr->session_state)
893 scr->session_state = WMCreatePLDictionary(NULL, NULL);
895 if (!wPreferences.flags.nodock) {
896 state = WMGetFromPLDictionary(scr->session_state, dDock);
897 scr->dock = wDockRestoreState(scr, state, WM_DOCK);
900 if (!wPreferences.flags.noclip) {
901 state = WMGetFromPLDictionary(scr->session_state, dClip);
902 scr->clip_icon = wClipRestoreState(scr, state);
905 if (!wPreferences.flags.nodrawer) {
906 if (!scr->dock->on_right_side) {
907 /* Drawer tile was created early in wScreenInit() -> wReadDefaults(). At
908 * that time, scr->dock was NULL and the tile was created as if we were on
909 * the right side. If we aren't, redo it now. */
910 assert(scr->drawer_tile);
911 RReleaseImage(scr->drawer_tile);
912 scr->drawer_tile = wDrawerMakeTile(scr, scr->icon_tile);
914 wDrawersRestoreState(scr);
917 wWorkspaceRestoreState(scr);
918 wScreenUpdateUsableArea(scr);
921 void wScreenSaveState(WScreen * scr)
923 WWindow *wwin;
924 char *str;
925 WMPropList *old_state, *foo;
927 make_keys();
929 /* save state of windows */
930 wwin = scr->focused_window;
931 while (wwin) {
932 wWindowSaveState(wwin);
933 wwin = wwin->prev;
936 if (wPreferences.flags.noupdates)
937 return;
939 old_state = scr->session_state;
940 scr->session_state = WMCreatePLDictionary(NULL, NULL);
942 WMPLSetCaseSensitive(True);
944 /* save dock state to file */
945 if (!wPreferences.flags.nodock) {
946 wDockSaveState(scr, old_state);
947 } else {
948 foo = WMGetFromPLDictionary(old_state, dDock);
949 if (foo != NULL)
950 WMPutInPLDictionary(scr->session_state, dDock, foo);
952 if (!wPreferences.flags.noclip) {
953 wClipSaveState(scr);
954 } else {
955 foo = WMGetFromPLDictionary(old_state, dClip);
956 if (foo != NULL)
957 WMPutInPLDictionary(scr->session_state, dClip, foo);
960 wWorkspaceSaveState(scr, old_state);
962 if (!wPreferences.flags.nodrawer) {
963 wDrawersSaveState(scr);
964 } else {
965 foo = WMGetFromPLDictionary(old_state, dDrawers);
966 if (foo != NULL)
967 WMPutInPLDictionary(scr->session_state, dDrawers, foo);
971 if (wPreferences.save_session_on_exit) {
972 wSessionSaveState(scr);
973 } else {
974 foo = WMGetFromPLDictionary(old_state, dApplications);
975 if (foo != NULL)
976 WMPutInPLDictionary(scr->session_state, dApplications, foo);
978 foo = WMGetFromPLDictionary(old_state, dWorkspace);
979 if (foo != NULL)
980 WMPutInPLDictionary(scr->session_state, dWorkspace, foo);
983 /* clean up */
984 WMPLSetCaseSensitive(False);
986 wMenuSaveState(scr);
988 if (w_global.screen_count == 1) {
989 str = wdefaultspathfordomain("WMState");
990 } else {
991 char buf[16];
992 snprintf(buf, sizeof(buf), "WMState.%i", scr->screen);
993 str = wdefaultspathfordomain(buf);
995 if (!WMWritePropListToFile(scr->session_state, str)) {
996 werror(_("could not save session state in %s"), str);
998 wfree(str);
999 WMReleasePropList(old_state);
1002 int wScreenBringInside(WScreen * scr, int *x, int *y, int width, int height)
1004 int moved = 0;
1005 int tol_w, tol_h;
1007 * With respect to the head that contains most of the window.
1009 int sx1, sy1, sx2, sy2;
1011 WMRect rect;
1012 int head, flags;
1014 rect.pos.x = *x;
1015 rect.pos.y = *y;
1016 rect.size.width = width;
1017 rect.size.height = height;
1019 head = wGetRectPlacementInfo(scr, rect, &flags);
1020 rect = wGetRectForHead(scr, head);
1022 sx1 = rect.pos.x;
1023 sy1 = rect.pos.y;
1024 sx2 = sx1 + rect.size.width;
1025 sy2 = sy1 + rect.size.height;
1027 #if 0 /* NOTE: gives funky group movement */
1028 if (flags & XFLAG_MULTIPLE) {
1030 * since we span multiple heads, pull window totaly inside
1032 if (*x < sx1)
1033 *x = sx1, moved = 1;
1034 else if (*x + width > sx2)
1035 *x = sx2 - width, moved = 1;
1037 if (*y < sy1)
1038 *y = sy1, moved = 1;
1039 else if (*y + height > sy2)
1040 *y = sy2 - height, moved = 1;
1042 return moved;
1044 #endif
1046 if (width > 20)
1047 tol_w = width / 2;
1048 else
1049 tol_w = 20;
1051 if (height > 20)
1052 tol_h = height / 2;
1053 else
1054 tol_h = 20;
1056 if (*x + width < sx1 + 10)
1057 *x = sx1 - tol_w, moved = 1;
1058 else if (*x >= sx2 - 10)
1059 *x = sx2 - tol_w - 1, moved = 1;
1061 if (*y < sy1 - height + 10)
1062 *y = sy1 - tol_h, moved = 1;
1063 else if (*y >= sy2 - 10)
1064 *y = sy2 - tol_h - 1, moved = 1;
1066 return moved;
1069 int wScreenKeepInside(WScreen * scr, int *x, int *y, int width, int height)
1071 int moved = 0;
1072 int sx1, sy1, sx2, sy2;
1073 WMRect rect;
1074 int head;
1076 rect.pos.x = *x;
1077 rect.pos.y = *y;
1078 rect.size.width = width;
1079 rect.size.height = height;
1081 head = wGetHeadForRect(scr, rect);
1082 rect = wGetRectForHead(scr, head);
1084 sx1 = rect.pos.x;
1085 sy1 = rect.pos.y;
1086 sx2 = sx1 + rect.size.width;
1087 sy2 = sy1 + rect.size.height;
1089 if (*x < sx1)
1090 *x = sx1, moved = 1;
1091 else if (*x + width > sx2)
1092 *x = sx2 - width, moved = 1;
1094 if (*y < sy1)
1095 *y = sy1, moved = 1;
1096 else if (*y + height > sy2)
1097 *y = sy2 - height, moved = 1;
1099 return moved;