WRaster: add functions to save image on disk
[wmaker-crm.git] / src / screen.c
blob6a7358fa7891d7ca407fc36b0e6bca35ac42c376
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 #ifdef USE_ICCCM_WMREPLACE
70 #define REPLACE_WM_TIMEOUT 15
71 #endif
73 #define STIPPLE_WIDTH 2
74 #define STIPPLE_HEIGHT 2
75 static char STIPPLE_DATA[] = { 0x02, 0x01 };
77 static int CantManageScreen = 0;
79 static WMPropList *dApplications = NULL;
80 static WMPropList *dWorkspace;
81 static WMPropList *dDock;
82 static WMPropList *dClip;
83 static WMPropList *dDrawers = NULL;
85 static void make_keys(void)
87 if (dApplications != NULL)
88 return;
90 dApplications = WMCreatePLString("Applications");
91 dWorkspace = WMCreatePLString("Workspace");
92 dDock = WMCreatePLString("Dock");
93 dClip = WMCreatePLString("Clip");
94 dDrawers = WMCreatePLString("Drawers");
98 * Support for ICCCM 2.0: Window Manager Replacement protocol
99 * See: http://www.x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html
101 * Basically, user should be able to dynamically change its window manager; this is done
102 * cooperatively through a special Selection ("WM_Sn" where 'n' is the X screen number)
104 * This function does 2 things:
105 * - it checks if this selection is already owned, which means that another window
106 * manager is running. If it is the case and user specified '--replace' on the command
107 * line, then it asks the WM to shut down;
108 * - when ok, it sets the selection ownership to ourself, so another window manager
109 * may ask us to shut down (this is handled in "event.c")
111 static Bool replace_existing_wm(WScreen *scr)
113 #ifdef USE_ICCCM_WMREPLACE
114 char atomName[16];
115 Window wm;
116 XSetWindowAttributes attribs;
117 XClientMessageEvent event;
118 unsigned long current_time;
119 int ret;
121 /* Try to acquire the atom named WM_S<screen> */
122 ret = snprintf(atomName, sizeof(atomName), "WM_S%d", scr->screen);
123 if (ret < 0 || ret == sizeof(atomName)) {
124 werror("out of memory trying to allocate window manager selection atom for screen %d", scr->screen);
125 return False;
128 scr->sn_atom = XInternAtom(dpy, atomName, False);
129 if (! scr->sn_atom)
130 return False;
132 /* Check if an existing window manager owns the selection */
133 wm = XGetSelectionOwner(dpy, scr->sn_atom);
134 if (wm) {
135 if (!wPreferences.flags.replace) {
136 wmessage(_("another window manager is running"));
137 wwarning(_("use the --replace flag to replace it"));
138 return False;
141 attribs.event_mask = StructureNotifyMask;
142 if (!XChangeWindowAttributes(dpy, wm, CWEventMask, &attribs))
143 wm = None;
145 #endif
147 /* for our window manager info notice board and the selection owner */
148 scr->info_window = XCreateSimpleWindow(dpy, scr->root_win, 0, 0, 10, 10, 0, 0, 0);
150 #ifdef USE_ICCCM_WMREPLACE
151 /* Try to acquire the selection */
152 current_time = CurrentTime;
153 ret = XSetSelectionOwner(dpy, scr->sn_atom, scr->info_window, current_time);
154 if (ret == BadAtom || ret == BadWindow)
155 return False;
157 /* Wait for other window manager to exit */
158 if (wm) {
159 unsigned long wait = 0;
160 unsigned long timeout = REPLACE_WM_TIMEOUT * 1000000L;
161 XEvent event;
163 while (wait < timeout) {
164 if (!(wait % 1000000))
165 wmessage(_("waiting %lus for other window manager to exit"), (timeout - wait) / 1000000L);
167 if (XCheckWindowEvent(dpy, wm, StructureNotifyMask, &event))
168 if (event.type == DestroyNotify)
169 break;
171 wusleep(100000);
172 wait += 100000;
175 if (wait >= timeout) {
176 wwarning(_("other window manager hasn't exited!"));
177 return False;
180 wmessage(_("replacing the other window manager"));
183 if (XGetSelectionOwner(dpy, scr->sn_atom) != scr->info_window)
184 return False;
186 event.type = ClientMessage;
187 event.message_type = scr->sn_atom;
188 event.format = 32;
189 event.data.l[0] = (long) current_time;
190 event.data.l[1] = (long) scr->sn_atom;
191 event.data.l[2] = (long) scr->info_window;
192 event.data.l[3] = (long) 0L;
193 event.data.l[4] = (long) 0L;
194 event.window = scr->root_win;
195 XSendEvent(dpy, scr->root_win, False, StructureNotifyMask, (XEvent *) &event);
196 #endif
198 return True;
202 *----------------------------------------------------------------------
203 * alreadyRunningError--
204 * X error handler used to catch errors when trying to do
205 * XSelectInput() on the root window. These errors probably mean that
206 * there already is some other window manager running.
208 * Returns:
209 * Nothing, unless something really evil happens...
211 * Side effects:
212 * CantManageScreen is set to 1;
213 *----------------------------------------------------------------------
215 static int alreadyRunningError(Display * dpy, XErrorEvent * error)
217 /* Parameter not used, but tell the compiler that it is ok */
218 (void) dpy;
219 (void) error;
221 CantManageScreen = 1;
222 return -1;
226 *----------------------------------------------------------------------
227 * allocButtonPixmaps--
228 * Allocate pixmaps used on window operation buttons (those in the
229 * titlebar). The pixmaps are linked to the program. If XPM is supported
230 * XPM pixmaps are used otherwise, equivalent bitmaps are used.
232 * Returns:
233 * Nothing
235 * Side effects:
236 * Allocates shared pixmaps for the screen. These pixmaps should
237 * not be freed by anybody.
238 *----------------------------------------------------------------------
240 static void allocButtonPixmaps(WScreen * scr)
242 WPixmap *pix;
244 /* create predefined pixmaps */
245 if (wPreferences.new_style == TS_NEXT) {
246 pix = wPixmapCreateFromXPMData(scr, NEXT_CLOSE_XPM);
247 } else {
248 pix = wPixmapCreateFromXPMData(scr, PRED_CLOSE_XPM);
250 if (pix)
251 pix->shared = 1;
252 scr->b_pixmaps[WBUT_CLOSE] = pix;
254 if (wPreferences.new_style == TS_NEXT) {
255 pix = wPixmapCreateFromXPMData(scr, NEXT_BROKEN_CLOSE_XPM);
256 } else {
257 pix = wPixmapCreateFromXPMData(scr, PRED_BROKEN_CLOSE_XPM);
259 if (pix)
260 pix->shared = 1;
261 scr->b_pixmaps[WBUT_BROKENCLOSE] = pix;
263 if (wPreferences.new_style == TS_NEXT) {
264 pix = wPixmapCreateFromXPMData(scr, NEXT_ICONIFY_XPM);
265 } else {
266 pix = wPixmapCreateFromXPMData(scr, PRED_ICONIFY_XPM);
268 if (pix)
269 pix->shared = 1;
270 scr->b_pixmaps[WBUT_ICONIFY] = pix;
271 #ifdef XKB_BUTTON_HINT
272 if (wPreferences.new_style == TS_NEXT) {
273 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP1_XPM);
274 } else {
275 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP1_XPM);
277 if (pix)
278 pix->shared = 1;
279 scr->b_pixmaps[WBUT_XKBGROUP1] = pix;
280 if (wPreferences.new_style == TS_NEXT) {
281 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP2_XPM);
282 } else {
283 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP2_XPM);
285 if (pix)
286 pix->shared = 1;
287 scr->b_pixmaps[WBUT_XKBGROUP2] = pix;
288 if (wPreferences.new_style == TS_NEXT) {
289 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP3_XPM);
290 } else {
291 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP3_XPM);
293 if (pix)
294 pix->shared = 1;
295 scr->b_pixmaps[WBUT_XKBGROUP3] = pix;
296 if (wPreferences.new_style == TS_NEXT) {
297 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP4_XPM);
298 } else {
299 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP4_XPM);
301 if (pix)
302 pix->shared = 1;
303 scr->b_pixmaps[WBUT_XKBGROUP4] = pix;
304 #endif
306 if (wPreferences.new_style == TS_NEXT) {
307 pix = wPixmapCreateFromXPMData(scr, NEXT_KILL_XPM);
308 } else {
309 pix = wPixmapCreateFromXPMData(scr, PRED_KILL_XPM);
311 if (pix)
312 pix->shared = 1;
313 scr->b_pixmaps[WBUT_KILL] = pix;
316 static int get_dot_mult(void)
318 int dot_mult;
321 * Since it's an int, the multiplier will grow only when
322 * a certain icon size is reached (1 for 64, 2 for 128,
323 * 3 for 192, and so on).
325 dot_mult = wPreferences.icon_size / 64;
327 if (dot_mult < 1)
328 dot_mult = 1;
330 return dot_mult;
333 static void draw_dot(WScreen * scr, Drawable d, int x, int y, GC gc)
335 int dot_mult;
337 dot_mult = get_dot_mult();
338 XSetForeground(dpy, gc, scr->black_pixel);
339 XFillRectangle(dpy, d, gc, x, y, x + (2 * dot_mult), y + (1 * dot_mult));
340 XFillRectangle(dpy, d, gc, x, y + (1 * dot_mult), x + (1 * dot_mult), y + (2 * dot_mult));
341 XSetForeground(dpy, gc, scr->white_pixel);
342 XFillRectangle(dpy, d, gc, x + (2 * dot_mult), y, x + (3 * dot_mult), y + (2 * dot_mult));
343 XFillRectangle(dpy, d, gc, x + (1 * dot_mult), y + (1 * dot_mult), x + (2 * dot_mult), y + (2 * dot_mult));
346 static WPixmap *make3Dots(WScreen * scr)
348 WPixmap *wpix;
349 GC gc2, gc;
350 XGCValues gcv;
351 Pixmap pix, mask;
352 int dot_mult;
354 dot_mult = get_dot_mult();
355 gc = scr->copy_gc;
356 pix = XCreatePixmap(dpy, scr->w_win, wPreferences.icon_size, wPreferences.icon_size, scr->w_depth);
357 XSetForeground(dpy, gc, scr->black_pixel);
358 XFillRectangle(dpy, pix, gc, 0, 0, wPreferences.icon_size, wPreferences.icon_size);
359 XSetForeground(dpy, gc, scr->white_pixel);
360 draw_dot(scr, pix, 4 * dot_mult, wPreferences.icon_size - (6 * dot_mult), gc);
361 draw_dot(scr, pix, 9 * dot_mult, wPreferences.icon_size - (6 * dot_mult), gc);
362 draw_dot(scr, pix, 14 * dot_mult, wPreferences.icon_size - (6 * dot_mult), gc);
364 mask = XCreatePixmap(dpy, scr->w_win, wPreferences.icon_size, wPreferences.icon_size, 1);
365 gcv.foreground = 0;
366 gcv.graphics_exposures = False;
367 gc2 = XCreateGC(dpy, mask, GCForeground | GCGraphicsExposures, &gcv);
368 XFillRectangle(dpy, mask, gc2, 0, 0, wPreferences.icon_size, wPreferences.icon_size);
369 XSetForeground(dpy, gc2, 1);
370 XFillRectangle(dpy, mask, gc2, 4 * dot_mult, wPreferences.icon_size - (6 * dot_mult), 3 * dot_mult, 2 * dot_mult);
371 XFillRectangle(dpy, mask, gc2, 9 * dot_mult, wPreferences.icon_size - (6 * dot_mult), 3 * dot_mult, 2 * dot_mult);
372 XFillRectangle(dpy, mask, gc2, 14 * dot_mult, wPreferences.icon_size - (6 * dot_mult), 3 * dot_mult, 2 * dot_mult);
374 XFreeGC(dpy, gc2);
376 wpix = wPixmapCreate(pix, mask);
377 wpix->shared = 1;
379 return wpix;
382 static void allocGCs(WScreen * scr)
384 XGCValues gcv;
385 XColor color;
386 int gcm;
388 scr->stipple_bitmap = XCreateBitmapFromData(dpy, scr->w_win, STIPPLE_DATA, STIPPLE_WIDTH, STIPPLE_HEIGHT);
390 gcv.stipple = scr->stipple_bitmap;
391 gcv.foreground = scr->white_pixel;
392 gcv.fill_style = FillStippled;
393 gcv.graphics_exposures = False;
394 gcm = GCForeground | GCStipple | GCFillStyle | GCGraphicsExposures;
395 scr->stipple_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
397 /* selected icon border GCs */
398 gcv.function = GXcopy;
399 gcv.foreground = scr->white_pixel;
400 gcv.background = scr->black_pixel;
401 gcv.line_width = 1;
402 gcv.line_style = LineDoubleDash;
403 gcv.fill_style = FillSolid;
404 gcv.dash_offset = 0;
405 gcv.dashes = 4;
406 gcv.graphics_exposures = False;
408 gcm = GCFunction | GCGraphicsExposures;
409 gcm |= GCForeground | GCBackground;
410 gcm |= GCLineWidth | GCLineStyle;
411 gcm |= GCFillStyle;
412 gcm |= GCDashOffset | GCDashList;
414 scr->icon_select_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
416 scr->menu_title_color[0] = WMRetainColor(scr->white);
418 /* don't retain scr->black here because we may alter its alpha */
419 scr->mtext_color = WMCreateRGBColor(scr->wmscreen, 0, 0, 0, True);
420 scr->dtext_color = WMCreateRGBColor(scr->wmscreen, 0, 0, 0, True);
422 /* frame GC */
423 wGetColor(scr, DEF_FRAME_COLOR, &color);
424 gcv.function = GXxor;
425 /* this will raise the probability of the XORed color being different
426 * of the original color in PseudoColor when not all color cells are
427 * initialized */
428 if (DefaultVisual(dpy, scr->screen)->class == PseudoColor)
429 gcv.plane_mask = (1 << (scr->depth - 1)) | 1;
430 else
431 gcv.plane_mask = AllPlanes;
432 gcv.foreground = color.pixel;
433 if (gcv.foreground == 0)
434 gcv.foreground = 1;
435 gcv.line_width = DEF_FRAME_THICKNESS;
436 gcv.subwindow_mode = IncludeInferiors;
437 gcv.graphics_exposures = False;
438 scr->frame_gc = XCreateGC(dpy, scr->root_win, GCForeground | GCGraphicsExposures
439 | GCFunction | GCSubwindowMode | GCLineWidth | GCPlaneMask, &gcv);
441 /* line GC */
442 gcv.foreground = color.pixel;
444 if (gcv.foreground == 0)
445 /* XOR:ing with a zero is not going to be of much use, so
446 in that case, we somewhat arbitrarily xor with 17 instead. */
447 gcv.foreground = 17;
449 gcv.function = GXxor;
450 gcv.subwindow_mode = IncludeInferiors;
451 gcv.line_width = 1;
452 gcv.cap_style = CapRound;
453 gcv.graphics_exposures = False;
454 gcm = GCForeground | GCFunction | GCSubwindowMode | GCLineWidth | GCCapStyle | GCGraphicsExposures;
455 scr->line_gc = XCreateGC(dpy, scr->root_win, gcm, &gcv);
457 scr->line_pixel = gcv.foreground;
459 /* copy GC */
460 gcv.foreground = scr->white_pixel;
461 gcv.background = scr->black_pixel;
462 gcv.graphics_exposures = False;
463 scr->copy_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
465 /* misc drawing GC */
466 gcv.graphics_exposures = False;
467 gcm = GCGraphicsExposures;
468 scr->draw_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
470 assert(scr->stipple_bitmap != None);
472 /* mono GC */
473 scr->mono_gc = XCreateGC(dpy, scr->stipple_bitmap, gcm, &gcv);
476 static void createPixmaps(WScreen * scr)
478 WPixmap *pix;
480 /* load pixmaps */
481 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_RADIO_INDICATOR_XBM_DATA,
482 (char *)MENU_RADIO_INDICATOR_XBM_DATA,
483 MENU_RADIO_INDICATOR_XBM_SIZE,
484 MENU_RADIO_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
485 if (pix != NULL)
486 pix->shared = 1;
487 scr->menu_radio_indicator = pix;
489 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_CHECK_INDICATOR_XBM_DATA,
490 (char *)MENU_CHECK_INDICATOR_XBM_DATA,
491 MENU_CHECK_INDICATOR_XBM_SIZE,
492 MENU_CHECK_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
493 if (pix != NULL)
494 pix->shared = 1;
495 scr->menu_check_indicator = pix;
497 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_MINI_INDICATOR_XBM_DATA,
498 (char *)MENU_MINI_INDICATOR_XBM_DATA,
499 MENU_MINI_INDICATOR_XBM_SIZE,
500 MENU_MINI_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
501 if (pix != NULL)
502 pix->shared = 1;
503 scr->menu_mini_indicator = pix;
505 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_HIDE_INDICATOR_XBM_DATA,
506 (char *)MENU_HIDE_INDICATOR_XBM_DATA,
507 MENU_HIDE_INDICATOR_XBM_SIZE,
508 MENU_HIDE_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
509 if (pix != NULL)
510 pix->shared = 1;
511 scr->menu_hide_indicator = pix;
513 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_SHADE_INDICATOR_XBM_DATA,
514 (char *)MENU_SHADE_INDICATOR_XBM_DATA,
515 MENU_SHADE_INDICATOR_XBM_SIZE,
516 MENU_SHADE_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
517 if (pix != NULL)
518 pix->shared = 1;
519 scr->menu_shade_indicator = pix;
521 create_logo_image(scr);
523 scr->dock_dots = make3Dots(scr);
525 /* titlebar button pixmaps */
526 allocButtonPixmaps(scr);
529 void create_logo_image(WScreen *scr)
531 RImage *image = get_icon_image(scr, "Logo", "WMPanel", 128);
533 if (!image) {
534 wwarning(_("could not load logo image for panels: %s"), RMessageForError(RErrorCode));
535 } else {
536 WMSetApplicationIconImage(scr->wmscreen, image);
537 RReleaseImage(image);
542 *----------------------------------------------------------------------
543 * createInternalWindows--
544 * Creates some windows used internally by the program. One to
545 * receive input focus when no other window can get it and another
546 * to display window geometry information during window resize/move.
548 * Returns:
549 * Nothing
551 * Side effects:
552 * Windows are created and some colors are allocated for the
553 * window background.
554 *----------------------------------------------------------------------
556 static void createInternalWindows(WScreen * scr)
558 int vmask;
559 XSetWindowAttributes attribs;
561 /* InputOnly window to get the focus when no other window can get it */
562 vmask = CWEventMask | CWOverrideRedirect;
563 attribs.event_mask = KeyPressMask | FocusChangeMask;
564 attribs.override_redirect = True;
565 scr->no_focus_win = XCreateWindow(dpy, scr->root_win, -10, -10, 4, 4, 0, 0,
566 InputOnly, CopyFromParent, vmask, &attribs);
567 XSelectInput(dpy, scr->no_focus_win, KeyPressMask | KeyReleaseMask);
568 XMapWindow(dpy, scr->no_focus_win);
570 XSetInputFocus(dpy, scr->no_focus_win, RevertToParent, CurrentTime);
572 /* shadow window for dock buttons */
573 vmask = CWBorderPixel | CWBackPixmap | CWBackPixel | CWCursor | CWSaveUnder | CWOverrideRedirect;
574 attribs.border_pixel = scr->black_pixel;
575 attribs.save_under = True;
576 attribs.override_redirect = True;
577 attribs.background_pixmap = None;
578 attribs.background_pixel = scr->white_pixel;
579 attribs.cursor = wPreferences.cursor[WCUR_NORMAL];
580 vmask |= CWColormap;
581 attribs.colormap = scr->w_colormap;
582 scr->dock_shadow =
583 XCreateWindow(dpy, scr->root_win, 0, 0, wPreferences.icon_size,
584 wPreferences.icon_size, 0, scr->w_depth, CopyFromParent, scr->w_visual, vmask, &attribs);
586 /* workspace name */
587 vmask = CWBackPixel | CWSaveUnder | CWOverrideRedirect | CWColormap | CWBorderPixel;
588 attribs.save_under = True;
589 attribs.override_redirect = True;
590 attribs.colormap = scr->w_colormap;
591 attribs.background_pixel = scr->icon_back_texture->normal.pixel;
592 attribs.border_pixel = 0; /* do not care */
593 scr->workspace_name =
594 XCreateWindow(dpy, scr->root_win, 0, 0, 10, 10, 0, scr->w_depth,
595 CopyFromParent, scr->w_visual, vmask, &attribs);
599 *----------------------------------------------------------------------
600 * wScreenInit--
601 * Initializes the window manager for the given screen and
602 * allocates a WScreen descriptor for it. Many resources are allocated
603 * for the screen and the root window is setup appropriately.
605 * Returns:
606 * The WScreen descriptor for the screen.
608 * Side effects:
609 * Many resources are allocated and the IconSize property is
610 * set on the root window.
611 * The program can be aborted if some fatal error occurs.
613 * TODO: User specifiable visual.
614 *----------------------------------------------------------------------
616 WScreen *wScreenInit(int screen_number)
618 WScreen *scr;
619 XIconSize icon_size[1];
620 RContextAttributes rattr = {};
621 long event_mask;
622 XErrorHandler oldHandler;
623 int i;
625 scr = wmalloc(sizeof(WScreen));
627 scr->stacking_list = WMCreateTreeBag();
629 /* initialize globals */
630 scr->screen = screen_number;
631 scr->root_win = RootWindow(dpy, screen_number);
632 scr->depth = DefaultDepth(dpy, screen_number);
633 scr->colormap = DefaultColormap(dpy, screen_number);
635 scr->scr_width = WidthOfScreen(ScreenOfDisplay(dpy, screen_number));
636 scr->scr_height = HeightOfScreen(ScreenOfDisplay(dpy, screen_number));
638 wInitXinerama(scr);
640 scr->usableArea = (WArea *) wmalloc(sizeof(WArea) * wXineramaHeads(scr));
641 scr->totalUsableArea = (WArea *) wmalloc(sizeof(WArea) * wXineramaHeads(scr));
643 for (i = 0; i < wXineramaHeads(scr); ++i) {
644 WMRect rect = wGetRectForHead(scr, i);
645 scr->usableArea[i].x1 = scr->totalUsableArea[i].x1 = rect.pos.x;
646 scr->usableArea[i].y1 = scr->totalUsableArea[i].y1 = rect.pos.y;
647 scr->usableArea[i].x2 = scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width;
648 scr->usableArea[i].y2 = scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
651 scr->fakeGroupLeaders = WMCreateArray(16);
653 CantManageScreen = 0;
654 oldHandler = XSetErrorHandler(alreadyRunningError);
656 /* Do we need to replace an existing window manager? */
657 if (!replace_existing_wm(scr)) {
658 XDestroyWindow(dpy, scr->info_window);
659 wfree(scr);
660 return NULL;
663 event_mask = EVENT_MASK;
664 XSelectInput(dpy, scr->root_win, event_mask);
666 #ifdef KEEP_XKB_LOCK_STATUS
667 /* Only GroupLock doesn't work correctly in my system since right-alt
668 * can change mode while holding it too - ]d
670 if (w_global.xext.xkb.supported) {
671 XkbSelectEvents(dpy, XkbUseCoreKbd, XkbStateNotifyMask, XkbStateNotifyMask);
673 #endif /* KEEP_XKB_LOCK_STATUS */
675 #ifdef USE_RANDR
676 if (w_global.xext.randr.supported)
677 XRRSelectInput(dpy, scr->root_win, RRScreenChangeNotifyMask);
678 #endif
680 XSync(dpy, False);
681 XSetErrorHandler(oldHandler);
683 if (CantManageScreen) {
684 wfree(scr);
685 return NULL;
688 XDefineCursor(dpy, scr->root_win, wPreferences.cursor[WCUR_ROOT]);
690 /* screen descriptor for raster graphic library */
691 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap;
692 rattr.render_mode = wPreferences.no_dithering ? RBestMatchRendering : RDitheredRendering;
694 /* if the std colormap stuff works ok, this will be ignored */
695 rattr.colors_per_channel = wPreferences.cmap_size;
696 if (rattr.colors_per_channel < 2)
697 rattr.colors_per_channel = 2;
699 /* Use standard colormap */
700 rattr.standard_colormap_mode = RUseStdColormap;
702 if (getWVisualID(screen_number) >= 0) {
703 rattr.flags |= RC_VisualID;
704 rattr.visualid = getWVisualID(screen_number);
707 scr->rcontext = RCreateContext(dpy, screen_number, &rattr);
709 if (!scr->rcontext && RErrorCode == RERR_STDCMAPFAIL) {
710 wwarning("%s", RMessageForError(RErrorCode));
712 rattr.flags &= ~RC_StandardColormap;
713 rattr.standard_colormap_mode = RUseStdColormap;
715 scr->rcontext = RCreateContext(dpy, screen_number, &rattr);
717 if (scr->rcontext == NULL) {
718 wfatal(_("can't create Context on screen %d, %s"),
719 screen_number, RMessageForError(RErrorCode));
720 goto abort_no_context;
723 scr->w_win = scr->rcontext->drawable;
724 scr->w_visual = scr->rcontext->visual;
725 scr->w_depth = scr->rcontext->depth;
726 scr->w_colormap = scr->rcontext->cmap;
728 /* create screen descriptor for WINGs */
729 scr->wmscreen = WMCreateScreenWithRContext(dpy, screen_number, scr->rcontext);
731 if (!scr->wmscreen) {
732 wfatal(_("could not initialize WINGs widget set"));
733 RDestroyContext(scr->rcontext);
734 abort_no_context:
735 WMFreeArray(scr->fakeGroupLeaders);
736 wfree(scr->totalUsableArea);
737 wfree(scr->usableArea);
738 WMFreeBag(scr->stacking_list);
739 wfree(scr);
740 return NULL;
743 scr->black = WMBlackColor(scr->wmscreen);
744 scr->white = WMWhiteColor(scr->wmscreen);
745 scr->gray = WMGrayColor(scr->wmscreen);
746 scr->darkGray = WMDarkGrayColor(scr->wmscreen);
748 scr->black_pixel = WMColorPixel(scr->black); /*scr->rcontext->black; */
749 scr->white_pixel = WMColorPixel(scr->white); /*scr->rcontext->white; */
750 scr->light_pixel = WMColorPixel(scr->gray);
751 scr->dark_pixel = WMColorPixel(scr->darkGray);
753 /* create GCs with default values */
754 allocGCs(scr);
756 /* read defaults for this screen */
757 wReadDefaults(scr, w_global.domain.wmaker->dictionary);
760 XColor xcol;
761 /* frame boder color */
762 wGetColor(scr, WMGetColorRGBDescription(scr->frame_border_color), &xcol);
763 scr->frame_border_pixel = xcol.pixel;
764 wGetColor(scr, WMGetColorRGBDescription(scr->frame_focused_border_color), &xcol);
765 scr->frame_focused_border_pixel = xcol.pixel;
766 wGetColor(scr, WMGetColorRGBDescription(scr->frame_selected_border_color), &xcol);
767 scr->frame_selected_border_pixel = xcol.pixel;
770 createInternalWindows(scr);
772 wNETWMInitStuff(scr);
774 /* create initial workspace */
775 wWorkspaceNew(scr);
777 /* create shared pixmaps */
778 createPixmaps(scr);
780 /* set icon sizes we can accept from clients */
781 icon_size[0].min_width = 8;
782 icon_size[0].min_height = 8;
783 icon_size[0].max_width = wPreferences.icon_size - 4;
784 icon_size[0].max_height = wPreferences.icon_size - 4;
785 icon_size[0].width_inc = 1;
786 icon_size[0].height_inc = 1;
787 XSetIconSizes(dpy, scr->root_win, icon_size, 1);
789 /* setup WindowMaker protocols property in the root window */
790 PropSetWMakerProtocols(scr->root_win);
792 /* setup our noticeboard */
793 XChangeProperty(dpy, scr->info_window, w_global.atom.wmaker.noticeboard,
794 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&scr->info_window, 1);
795 XChangeProperty(dpy, scr->root_win, w_global.atom.wmaker.noticeboard,
796 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&scr->info_window, 1);
798 #ifdef BALLOON_TEXT
799 /* initialize balloon text stuff */
800 wBalloonInitialize(scr);
801 #endif
803 scr->info_text_font = WMBoldSystemFontOfSize(scr->wmscreen, 12);
805 scr->tech_draw_font = XLoadQueryFont(dpy, "-adobe-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*");
806 if (!scr->tech_draw_font)
807 scr->tech_draw_font = XLoadQueryFont(dpy, "fixed");
809 scr->gview = WCreateGeometryView(scr->wmscreen);
810 WMRealizeWidget(scr->gview);
812 wScreenUpdateUsableArea(scr);
814 return scr;
817 void wScreenUpdateUsableArea(WScreen *scr)
820 * scr->totalUsableArea[] will become the usableArea used for Windowplacement,
821 * scr->usableArea[] will be used for iconplacement, hence no iconyard nor
822 * border.
825 WArea area;
826 int i;
827 unsigned long tmp_area, best_area = 0;
828 unsigned int size = wPreferences.workspace_border_size;
829 unsigned int position = wPreferences.workspace_border_position;
831 for (i = 0; i < wXineramaHeads(scr); ++i) {
832 WMRect rect = wGetRectForHead(scr, i);
833 scr->totalUsableArea[i].x1 = rect.pos.x;
834 scr->totalUsableArea[i].y1 = rect.pos.y;
835 scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width;
836 scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
838 if (wNETWMGetUsableArea(scr, i, &area)) {
839 scr->totalUsableArea[i].x1 = WMAX(scr->totalUsableArea[i].x1, area.x1);
840 scr->totalUsableArea[i].y1 = WMAX(scr->totalUsableArea[i].y1, area.y1);
841 scr->totalUsableArea[i].x2 = WMIN(scr->totalUsableArea[i].x2, area.x2);
842 scr->totalUsableArea[i].y2 = WMIN(scr->totalUsableArea[i].y2, area.y2);
845 scr->usableArea[i] = scr->totalUsableArea[i];
847 if (wPreferences.no_window_over_icons) {
848 if (wPreferences.icon_yard & IY_VERT) {
849 if (wPreferences.icon_yard & IY_RIGHT)
850 scr->totalUsableArea[i].x2 -= wPreferences.icon_size;
851 else
852 scr->totalUsableArea[i].x1 += wPreferences.icon_size;
853 } else {
854 if (wPreferences.icon_yard & IY_TOP)
855 scr->totalUsableArea[i].y1 += wPreferences.icon_size;
856 else
857 scr->totalUsableArea[i].y2 -= wPreferences.icon_size;
861 if (scr->totalUsableArea[i].x2 - scr->totalUsableArea[i].x1 < rect.size.width / 2) {
862 scr->totalUsableArea[i].x1 = rect.pos.x;
863 scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width;
866 if (scr->totalUsableArea[i].y2 - scr->totalUsableArea[i].y1 < rect.size.height / 2) {
867 scr->totalUsableArea[i].y1 = rect.pos.y;
868 scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
871 tmp_area = (scr->totalUsableArea[i].x2 - scr->totalUsableArea[i].x1) *
872 (scr->totalUsableArea[i].y2 - scr->totalUsableArea[i].y1);
874 if (tmp_area > best_area) {
875 best_area = tmp_area;
876 area = scr->totalUsableArea[i];
879 if (size > 0 && position != WB_NONE) {
880 if (position & WB_LEFTRIGHT) {
881 scr->totalUsableArea[i].x1 += size;
882 scr->totalUsableArea[i].x2 -= size;
884 if (position & WB_TOPBOTTOM) {
885 scr->totalUsableArea[i].y1 += size;
886 scr->totalUsableArea[i].y2 -= size;
891 if (wPreferences.auto_arrange_icons)
892 wArrangeIcons(scr, True);
895 void wScreenRestoreState(WScreen * scr)
897 WMPropList *state;
898 char *path;
900 OpenRootMenu(scr, -10000, -10000, False);
901 wMenuUnmap(scr->root_menu);
903 make_keys();
905 if (w_global.screen_count == 1) {
906 path = wdefaultspathfordomain("WMState");
907 } else {
908 char buf[16];
909 snprintf(buf, sizeof(buf), "WMState.%i", scr->screen);
910 path = wdefaultspathfordomain(buf);
912 scr->session_state = WMReadPropListFromFile(path);
913 wfree(path);
914 if (!scr->session_state && w_global.screen_count > 1) {
915 path = wdefaultspathfordomain("WMState");
916 scr->session_state = WMReadPropListFromFile(path);
917 wfree(path);
920 if (!scr->session_state)
921 scr->session_state = WMCreatePLDictionary(NULL, NULL);
923 if (!wPreferences.flags.nodock) {
924 state = WMGetFromPLDictionary(scr->session_state, dDock);
925 scr->dock = wDockRestoreState(scr, state, WM_DOCK);
928 if (!wPreferences.flags.noclip) {
929 state = WMGetFromPLDictionary(scr->session_state, dClip);
930 scr->clip_icon = wClipRestoreState(scr, state);
933 if (!wPreferences.flags.nodrawer) {
934 if (!scr->dock->on_right_side) {
935 /* Drawer tile was created early in wScreenInit() -> wReadDefaults(). At
936 * that time, scr->dock was NULL and the tile was created as if we were on
937 * the right side. If we aren't, redo it now. */
938 assert(scr->drawer_tile);
939 RReleaseImage(scr->drawer_tile);
940 scr->drawer_tile = wDrawerMakeTile(scr, scr->icon_tile);
942 wDrawersRestoreState(scr);
945 wWorkspaceRestoreState(scr);
946 wScreenUpdateUsableArea(scr);
949 void wScreenSaveState(WScreen * scr)
951 WWindow *wwin;
952 char *str;
953 WMPropList *old_state, *foo;
955 make_keys();
957 /* save state of windows */
958 wwin = scr->focused_window;
959 while (wwin) {
960 wWindowSaveState(wwin);
961 wwin = wwin->prev;
964 if (wPreferences.flags.noupdates)
965 return;
967 old_state = scr->session_state;
968 scr->session_state = WMCreatePLDictionary(NULL, NULL);
970 WMPLSetCaseSensitive(True);
972 /* save dock state to file */
973 if (!wPreferences.flags.nodock) {
974 wDockSaveState(scr, old_state);
975 } else {
976 foo = WMGetFromPLDictionary(old_state, dDock);
977 if (foo != NULL)
978 WMPutInPLDictionary(scr->session_state, dDock, foo);
980 if (!wPreferences.flags.noclip) {
981 wClipSaveState(scr);
982 } else {
983 foo = WMGetFromPLDictionary(old_state, dClip);
984 if (foo != NULL)
985 WMPutInPLDictionary(scr->session_state, dClip, foo);
988 wWorkspaceSaveState(scr, old_state);
990 if (!wPreferences.flags.nodrawer) {
991 wDrawersSaveState(scr);
992 } else {
993 foo = WMGetFromPLDictionary(old_state, dDrawers);
994 if (foo != NULL)
995 WMPutInPLDictionary(scr->session_state, dDrawers, foo);
999 if (wPreferences.save_session_on_exit) {
1000 wSessionSaveState(scr);
1001 } else {
1002 foo = WMGetFromPLDictionary(old_state, dApplications);
1003 if (foo != NULL)
1004 WMPutInPLDictionary(scr->session_state, dApplications, foo);
1006 foo = WMGetFromPLDictionary(old_state, dWorkspace);
1007 if (foo != NULL)
1008 WMPutInPLDictionary(scr->session_state, dWorkspace, foo);
1011 /* clean up */
1012 WMPLSetCaseSensitive(False);
1014 wMenuSaveState(scr);
1016 if (w_global.screen_count == 1) {
1017 str = wdefaultspathfordomain("WMState");
1018 } else {
1019 char buf[16];
1020 snprintf(buf, sizeof(buf), "WMState.%i", scr->screen);
1021 str = wdefaultspathfordomain(buf);
1023 if (!WMWritePropListToFile(scr->session_state, str)) {
1024 werror(_("could not save session state in %s"), str);
1026 wfree(str);
1027 WMReleasePropList(old_state);
1030 int wScreenBringInside(WScreen * scr, int *x, int *y, int width, int height)
1032 int moved = 0;
1033 int tol_w, tol_h;
1035 * With respect to the head that contains most of the window.
1037 int sx1, sy1, sx2, sy2;
1039 WMRect rect;
1040 int head, flags;
1042 rect.pos.x = *x;
1043 rect.pos.y = *y;
1044 rect.size.width = width;
1045 rect.size.height = height;
1047 head = wGetRectPlacementInfo(scr, rect, &flags);
1048 rect = wGetRectForHead(scr, head);
1050 sx1 = rect.pos.x;
1051 sy1 = rect.pos.y;
1052 sx2 = sx1 + rect.size.width;
1053 sy2 = sy1 + rect.size.height;
1055 #if 0 /* NOTE: gives funky group movement */
1056 if (flags & XFLAG_MULTIPLE) {
1058 * since we span multiple heads, pull window totaly inside
1060 if (*x < sx1)
1061 *x = sx1, moved = 1;
1062 else if (*x + width > sx2)
1063 *x = sx2 - width, moved = 1;
1065 if (*y < sy1)
1066 *y = sy1, moved = 1;
1067 else if (*y + height > sy2)
1068 *y = sy2 - height, moved = 1;
1070 return moved;
1072 #endif
1074 if (width > 20)
1075 tol_w = width / 2;
1076 else
1077 tol_w = 20;
1079 if (height > 20)
1080 tol_h = height / 2;
1081 else
1082 tol_h = 20;
1084 if (*x + width < sx1 + 10)
1085 *x = sx1 - tol_w, moved = 1;
1086 else if (*x >= sx2 - 10)
1087 *x = sx2 - tol_w - 1, moved = 1;
1089 if (*y < sy1 - height + 10)
1090 *y = sy1 - tol_h, moved = 1;
1091 else if (*y >= sy2 - 10)
1092 *y = sy2 - tol_h - 1, moved = 1;
1094 return moved;
1097 int wScreenKeepInside(WScreen * scr, int *x, int *y, int width, int height)
1099 int moved = 0;
1100 int sx1, sy1, sx2, sy2;
1101 WMRect rect;
1102 int head;
1104 rect.pos.x = *x;
1105 rect.pos.y = *y;
1106 rect.size.width = width;
1107 rect.size.height = height;
1109 head = wGetHeadForRect(scr, rect);
1110 rect = wGetRectForHead(scr, head);
1112 sx1 = rect.pos.x;
1113 sy1 = rect.pos.y;
1114 sx2 = sx1 + rect.size.width;
1115 sy2 = sy1 + rect.size.height;
1117 if (*x < sx1)
1118 *x = sx1, moved = 1;
1119 else if (*x + width > sx2)
1120 *x = sx2 - width, moved = 1;
1122 if (*y < sy1)
1123 *y = sy1, moved = 1;
1124 else if (*y + height > sy2)
1125 *y = sy2 - height, moved = 1;
1127 return moved;