WPrefs: fix keyboard shortcut sorting order
[wmaker-crm.git] / src / screen.c
blob66be4a9d8a79865fa68a448f4acea39ddf2374f6
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>
27 #include <time.h>
28 #include <errno.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
32 #include <X11/Xlib.h>
33 #include <X11/Xutil.h>
34 #include <X11/Xatom.h>
35 #ifdef KEEP_XKB_LOCK_STATUS
36 #include <X11/XKBlib.h>
37 #endif /* KEEP_XKB_LOCK_STATUS */
38 #ifdef USE_RANDR
39 #include <X11/extensions/Xrandr.h>
40 #endif
42 #include <wraster.h>
43 #include "WindowMaker.h"
44 #include "def_pixmaps.h"
45 #include "screen.h"
46 #include "texture.h"
47 #include "pixmap.h"
48 #include "menu.h"
49 #include "window.h"
50 #include "main.h"
51 #include "actions.h"
52 #include "properties.h"
53 #include "dock.h"
54 #include "resources.h"
55 #include "workspace.h"
56 #include "session.h"
57 #include "balloon.h"
58 #include "geomview.h"
59 #include "wmspec.h"
60 #include "rootmenu.h"
61 #include "misc.h"
63 #include "xinerama.h"
65 #include <WINGs/WUtil.h>
66 #include <WINGs/WINGsP.h>
68 #include "defaults.h"
70 #define EVENT_MASK (LeaveWindowMask|EnterWindowMask|PropertyChangeMask\
71 |SubstructureNotifyMask|PointerMotionMask \
72 |SubstructureRedirectMask|ButtonPressMask|ButtonReleaseMask\
73 |KeyPressMask|KeyReleaseMask)
75 #ifdef USE_ICCCM_WMREPLACE
76 #define REPLACE_WM_TIMEOUT 15
77 #endif
79 #define STIPPLE_WIDTH 2
80 #define STIPPLE_HEIGHT 2
81 static char STIPPLE_DATA[] = { 0x02, 0x01 };
83 static int CantManageScreen = 0;
85 static WMPropList *dApplications = NULL;
86 static WMPropList *dWorkspace;
87 static WMPropList *dDock;
88 static WMPropList *dClip;
89 static WMPropList *dDrawers = NULL;
91 static void make_keys(void)
93 if (dApplications != NULL)
94 return;
96 dApplications = WMCreatePLString("Applications");
97 dWorkspace = WMCreatePLString("Workspace");
98 dDock = WMCreatePLString("Dock");
99 dClip = WMCreatePLString("Clip");
100 dDrawers = WMCreatePLString("Drawers");
104 * Support for ICCCM 2.0: Window Manager Replacement protocol
105 * See: http://www.x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html
107 * Basically, user should be able to dynamically change its window manager; this is done
108 * cooperatively through a special Selection ("WM_Sn" where 'n' is the X screen number)
110 * This function does 2 things:
111 * - it checks if this selection is already owned, which means that another window
112 * manager is running. If it is the case and user specified '--replace' on the command
113 * line, then it asks the WM to shut down;
114 * - when ok, it sets the selection ownership to ourself, so another window manager
115 * may ask us to shut down (this is handled in "event.c")
117 static Bool replace_existing_wm(WScreen *scr)
119 #ifdef USE_ICCCM_WMREPLACE
120 char atomName[16];
121 Window wm;
122 XSetWindowAttributes attribs;
123 XClientMessageEvent event;
124 unsigned long current_time;
125 int ret;
127 /* Try to acquire the atom named WM_S<screen> */
128 ret = snprintf(atomName, sizeof(atomName), "WM_S%d", scr->screen);
129 if (ret < 0 || ret == sizeof(atomName)) {
130 werror("out of memory trying to allocate window manager selection atom for screen %d", scr->screen);
131 return False;
134 scr->sn_atom = XInternAtom(dpy, atomName, False);
135 if (! scr->sn_atom)
136 return False;
138 /* Check if an existing window manager owns the selection */
139 wm = XGetSelectionOwner(dpy, scr->sn_atom);
140 if (wm) {
141 if (!wPreferences.flags.replace) {
142 wmessage(_("another window manager is running"));
143 wwarning(_("use the --replace flag to replace it"));
144 return False;
147 attribs.event_mask = StructureNotifyMask;
148 if (!XChangeWindowAttributes(dpy, wm, CWEventMask, &attribs))
149 wm = None;
151 #endif
153 /* for our window manager info notice board and the selection owner */
154 scr->info_window = XCreateSimpleWindow(dpy, scr->root_win, 0, 0, 10, 10, 0, 0, 0);
156 #ifdef USE_ICCCM_WMREPLACE
157 /* Try to acquire the selection */
158 current_time = CurrentTime;
159 ret = XSetSelectionOwner(dpy, scr->sn_atom, scr->info_window, current_time);
160 if (ret == BadAtom || ret == BadWindow)
161 return False;
163 /* Wait for other window manager to exit */
164 if (wm) {
165 unsigned long wait = 0;
166 unsigned long timeout = REPLACE_WM_TIMEOUT * 1000000L;
167 XEvent event;
169 while (wait < timeout) {
170 if (!(wait % 1000000))
171 wmessage(_("waiting %lus for other window manager to exit"), (timeout - wait) / 1000000L);
173 if (XCheckWindowEvent(dpy, wm, StructureNotifyMask, &event))
174 if (event.type == DestroyNotify)
175 break;
177 wusleep(100000);
178 wait += 100000;
181 if (wait >= timeout) {
182 wwarning(_("other window manager hasn't exited!"));
183 return False;
186 wmessage(_("replacing the other window manager"));
189 if (XGetSelectionOwner(dpy, scr->sn_atom) != scr->info_window)
190 return False;
192 event.type = ClientMessage;
193 event.message_type = scr->sn_atom;
194 event.format = 32;
195 event.data.l[0] = (long) current_time;
196 event.data.l[1] = (long) scr->sn_atom;
197 event.data.l[2] = (long) scr->info_window;
198 event.data.l[3] = (long) 0L;
199 event.data.l[4] = (long) 0L;
200 event.window = scr->root_win;
201 XSendEvent(dpy, scr->root_win, False, StructureNotifyMask, (XEvent *) &event);
202 #endif
204 return True;
208 *----------------------------------------------------------------------
209 * alreadyRunningError--
210 * X error handler used to catch errors when trying to do
211 * XSelectInput() on the root window. These errors probably mean that
212 * there already is some other window manager running.
214 * Returns:
215 * Nothing, unless something really evil happens...
217 * Side effects:
218 * CantManageScreen is set to 1;
219 *----------------------------------------------------------------------
221 static int alreadyRunningError(Display * dpy, XErrorEvent * error)
223 /* Parameter not used, but tell the compiler that it is ok */
224 (void) dpy;
225 (void) error;
227 CantManageScreen = 1;
228 return -1;
232 *----------------------------------------------------------------------
233 * allocButtonPixmaps--
234 * Allocate pixmaps used on window operation buttons (those in the
235 * titlebar). The pixmaps are linked to the program. If XPM is supported
236 * XPM pixmaps are used otherwise, equivalent bitmaps are used.
238 * Returns:
239 * Nothing
241 * Side effects:
242 * Allocates shared pixmaps for the screen. These pixmaps should
243 * not be freed by anybody.
244 *----------------------------------------------------------------------
246 static void allocButtonPixmaps(WScreen * scr)
248 WPixmap *pix;
250 /* create predefined pixmaps */
251 if (wPreferences.new_style == TS_NEXT) {
252 pix = wPixmapCreateFromXPMData(scr, NEXT_CLOSE_XPM);
253 } else {
254 pix = wPixmapCreateFromXPMData(scr, PRED_CLOSE_XPM);
256 if (pix)
257 pix->shared = 1;
258 scr->b_pixmaps[WBUT_CLOSE] = pix;
260 if (wPreferences.new_style == TS_NEXT) {
261 pix = wPixmapCreateFromXPMData(scr, NEXT_BROKEN_CLOSE_XPM);
262 } else {
263 pix = wPixmapCreateFromXPMData(scr, PRED_BROKEN_CLOSE_XPM);
265 if (pix)
266 pix->shared = 1;
267 scr->b_pixmaps[WBUT_BROKENCLOSE] = pix;
269 if (wPreferences.new_style == TS_NEXT) {
270 pix = wPixmapCreateFromXPMData(scr, NEXT_ICONIFY_XPM);
271 } else {
272 pix = wPixmapCreateFromXPMData(scr, PRED_ICONIFY_XPM);
274 if (pix)
275 pix->shared = 1;
276 scr->b_pixmaps[WBUT_ICONIFY] = pix;
277 #ifdef XKB_BUTTON_HINT
278 if (wPreferences.new_style == TS_NEXT) {
279 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP1_XPM);
280 } else {
281 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP1_XPM);
283 if (pix)
284 pix->shared = 1;
285 scr->b_pixmaps[WBUT_XKBGROUP1] = pix;
286 if (wPreferences.new_style == TS_NEXT) {
287 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP2_XPM);
288 } else {
289 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP2_XPM);
291 if (pix)
292 pix->shared = 1;
293 scr->b_pixmaps[WBUT_XKBGROUP2] = pix;
294 if (wPreferences.new_style == TS_NEXT) {
295 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP3_XPM);
296 } else {
297 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP3_XPM);
299 if (pix)
300 pix->shared = 1;
301 scr->b_pixmaps[WBUT_XKBGROUP3] = pix;
302 if (wPreferences.new_style == TS_NEXT) {
303 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP4_XPM);
304 } else {
305 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP4_XPM);
307 if (pix)
308 pix->shared = 1;
309 scr->b_pixmaps[WBUT_XKBGROUP4] = pix;
310 #endif
312 if (wPreferences.new_style == TS_NEXT) {
313 pix = wPixmapCreateFromXPMData(scr, NEXT_KILL_XPM);
314 } else {
315 pix = wPixmapCreateFromXPMData(scr, PRED_KILL_XPM);
317 if (pix)
318 pix->shared = 1;
319 scr->b_pixmaps[WBUT_KILL] = pix;
322 static int get_dot_mult(void)
324 int dot_mult;
327 * Since it's an int, the multiplier will grow only when
328 * a certain icon size is reached (1 for 64, 2 for 128,
329 * 3 for 192, and so on).
331 dot_mult = wPreferences.icon_size / 64;
333 if (dot_mult < 1)
334 dot_mult = 1;
336 return dot_mult;
339 static void draw_dot(WScreen * scr, Drawable d, int x, int y, GC gc)
341 int dot_mult;
343 dot_mult = get_dot_mult();
344 XSetForeground(dpy, gc, scr->black_pixel);
345 XFillRectangle(dpy, d, gc, x, y, x + (2 * dot_mult), y + (1 * dot_mult));
346 XFillRectangle(dpy, d, gc, x, y + (1 * dot_mult), x + (1 * dot_mult), y + (2 * dot_mult));
347 XSetForeground(dpy, gc, scr->white_pixel);
348 XFillRectangle(dpy, d, gc, x + (2 * dot_mult), y, x + (3 * dot_mult), y + (2 * dot_mult));
349 XFillRectangle(dpy, d, gc, x + (1 * dot_mult), y + (1 * dot_mult), x + (2 * dot_mult), y + (2 * dot_mult));
352 static WPixmap *make3Dots(WScreen * scr)
354 WPixmap *wpix;
355 GC gc2, gc;
356 XGCValues gcv;
357 Pixmap pix, mask;
358 int dot_mult;
360 dot_mult = get_dot_mult();
361 gc = scr->copy_gc;
362 pix = XCreatePixmap(dpy, scr->w_win, wPreferences.icon_size, wPreferences.icon_size, scr->w_depth);
363 XSetForeground(dpy, gc, scr->black_pixel);
364 XFillRectangle(dpy, pix, gc, 0, 0, wPreferences.icon_size, wPreferences.icon_size);
365 XSetForeground(dpy, gc, scr->white_pixel);
366 draw_dot(scr, pix, 4 * dot_mult, wPreferences.icon_size - (6 * dot_mult), gc);
367 draw_dot(scr, pix, 9 * dot_mult, wPreferences.icon_size - (6 * dot_mult), gc);
368 draw_dot(scr, pix, 14 * dot_mult, wPreferences.icon_size - (6 * dot_mult), gc);
370 mask = XCreatePixmap(dpy, scr->w_win, wPreferences.icon_size, wPreferences.icon_size, 1);
371 gcv.foreground = 0;
372 gcv.graphics_exposures = False;
373 gc2 = XCreateGC(dpy, mask, GCForeground | GCGraphicsExposures, &gcv);
374 XFillRectangle(dpy, mask, gc2, 0, 0, wPreferences.icon_size, wPreferences.icon_size);
375 XSetForeground(dpy, gc2, 1);
376 XFillRectangle(dpy, mask, gc2, 4 * dot_mult, wPreferences.icon_size - (6 * dot_mult), 3 * dot_mult, 2 * dot_mult);
377 XFillRectangle(dpy, mask, gc2, 9 * dot_mult, wPreferences.icon_size - (6 * dot_mult), 3 * dot_mult, 2 * dot_mult);
378 XFillRectangle(dpy, mask, gc2, 14 * dot_mult, wPreferences.icon_size - (6 * dot_mult), 3 * dot_mult, 2 * dot_mult);
380 XFreeGC(dpy, gc2);
382 wpix = wPixmapCreate(pix, mask);
383 wpix->shared = 1;
385 return wpix;
388 static void allocGCs(WScreen * scr)
390 XGCValues gcv;
391 XColor color;
392 int gcm;
394 scr->stipple_bitmap = XCreateBitmapFromData(dpy, scr->w_win, STIPPLE_DATA, STIPPLE_WIDTH, STIPPLE_HEIGHT);
396 gcv.stipple = scr->stipple_bitmap;
397 gcv.foreground = scr->white_pixel;
398 gcv.fill_style = FillStippled;
399 gcv.graphics_exposures = False;
400 gcm = GCForeground | GCStipple | GCFillStyle | GCGraphicsExposures;
401 scr->stipple_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
403 /* selected icon border GCs */
404 gcv.function = GXcopy;
405 gcv.foreground = scr->white_pixel;
406 gcv.background = scr->black_pixel;
407 gcv.line_width = 1;
408 gcv.line_style = LineDoubleDash;
409 gcv.fill_style = FillSolid;
410 gcv.dash_offset = 0;
411 gcv.dashes = 4;
412 gcv.graphics_exposures = False;
414 gcm = GCFunction | GCGraphicsExposures;
415 gcm |= GCForeground | GCBackground;
416 gcm |= GCLineWidth | GCLineStyle;
417 gcm |= GCFillStyle;
418 gcm |= GCDashOffset | GCDashList;
420 scr->icon_select_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
422 scr->menu_title_color[0] = WMRetainColor(scr->white);
424 /* don't retain scr->black here because we may alter its alpha */
425 scr->mtext_color = WMCreateRGBColor(scr->wmscreen, 0, 0, 0, True);
426 scr->dtext_color = WMCreateRGBColor(scr->wmscreen, 0, 0, 0, True);
428 /* frame GC */
429 wGetColor(scr, DEF_FRAME_COLOR, &color);
430 gcv.function = GXxor;
431 /* this will raise the probability of the XORed color being different
432 * of the original color in PseudoColor when not all color cells are
433 * initialized */
434 if (DefaultVisual(dpy, scr->screen)->class == PseudoColor)
435 gcv.plane_mask = (1 << (scr->depth - 1)) | 1;
436 else
437 gcv.plane_mask = AllPlanes;
438 gcv.foreground = color.pixel;
439 if (gcv.foreground == 0)
440 gcv.foreground = 1;
441 gcv.line_width = DEF_FRAME_THICKNESS;
442 gcv.subwindow_mode = IncludeInferiors;
443 gcv.graphics_exposures = False;
444 scr->frame_gc = XCreateGC(dpy, scr->root_win, GCForeground | GCGraphicsExposures
445 | GCFunction | GCSubwindowMode | GCLineWidth | GCPlaneMask, &gcv);
447 /* line GC */
448 gcv.foreground = color.pixel;
450 if (gcv.foreground == 0)
451 /* XOR:ing with a zero is not going to be of much use, so
452 in that case, we somewhat arbitrarily xor with 17 instead. */
453 gcv.foreground = 17;
455 gcv.function = GXxor;
456 gcv.subwindow_mode = IncludeInferiors;
457 gcv.line_width = 1;
458 gcv.cap_style = CapRound;
459 gcv.graphics_exposures = False;
460 gcm = GCForeground | GCFunction | GCSubwindowMode | GCLineWidth | GCCapStyle | GCGraphicsExposures;
461 scr->line_gc = XCreateGC(dpy, scr->root_win, gcm, &gcv);
463 scr->line_pixel = gcv.foreground;
465 /* copy GC */
466 gcv.foreground = scr->white_pixel;
467 gcv.background = scr->black_pixel;
468 gcv.graphics_exposures = False;
469 scr->copy_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
471 /* misc drawing GC */
472 gcv.graphics_exposures = False;
473 gcm = GCGraphicsExposures;
474 scr->draw_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
476 assert(scr->stipple_bitmap != None);
478 /* mono GC */
479 scr->mono_gc = XCreateGC(dpy, scr->stipple_bitmap, gcm, &gcv);
482 static void createPixmaps(WScreen * scr)
484 WPixmap *pix;
486 /* load pixmaps */
487 #define LOADPIXMAPINDICATOR(XBM,W,I) {\
488 pix = wPixmapCreateFromXBMData(scr, (char *)XBM, (char *)XBM,\
489 W, MENU_SNAP_INDICATOR_H_XBM_SIZE,\
490 scr->black_pixel, scr->white_pixel);\
491 if (pix != NULL)\
492 pix->shared = 1;\
493 scr->I = pix;}
495 LOADPIXMAPINDICATOR(MENU_RADIO_INDICATOR_XBM_DATA, MENU_RADIO_INDICATOR_XBM_SIZE, menu_radio_indicator)
496 LOADPIXMAPINDICATOR(MENU_CHECK_INDICATOR_XBM_DATA, MENU_CHECK_INDICATOR_XBM_SIZE, menu_check_indicator)
497 LOADPIXMAPINDICATOR(MENU_MINI_INDICATOR_XBM_DATA, MENU_MINI_INDICATOR_XBM_SIZE, menu_mini_indicator)
498 LOADPIXMAPINDICATOR(MENU_HIDE_INDICATOR_XBM_DATA, MENU_HIDE_INDICATOR_XBM_SIZE, menu_hide_indicator)
499 LOADPIXMAPINDICATOR(MENU_SHADE_INDICATOR_XBM_DATA, MENU_SHADE_INDICATOR_XBM_SIZE, menu_shade_indicator)
501 LOADPIXMAPINDICATOR(MENU_SNAP_V_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_vertically_indicator)
502 LOADPIXMAPINDICATOR(MENU_SNAP_H_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_horizontally_indicator)
503 LOADPIXMAPINDICATOR(MENU_SNAP_RH_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_rh_indicator)
504 LOADPIXMAPINDICATOR(MENU_SNAP_LH_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_lh_indicator)
505 LOADPIXMAPINDICATOR(MENU_SNAP_TH_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_th_indicator)
506 LOADPIXMAPINDICATOR(MENU_SNAP_BH_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_bh_indicator)
507 LOADPIXMAPINDICATOR(MENU_SNAP_TL_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_tl_indicator)
508 LOADPIXMAPINDICATOR(MENU_SNAP_TR_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_tr_indicator)
509 LOADPIXMAPINDICATOR(MENU_SNAP_BL_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_bl_indicator)
510 LOADPIXMAPINDICATOR(MENU_SNAP_BR_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_br_indicator)
511 LOADPIXMAPINDICATOR(MENU_SNAP_TILED_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_tiled_indicator)
513 #undef LOADPIXMAPINDICATOR
515 create_logo_image(scr);
517 scr->dock_dots = make3Dots(scr);
519 /* titlebar button pixmaps */
520 allocButtonPixmaps(scr);
523 void create_logo_image(WScreen *scr)
525 RImage *image = get_icon_image(scr, "Logo", "WMPanel", 128);
527 if (!image) {
528 wwarning(_("could not load logo image for panels: %s"), RMessageForError(RErrorCode));
529 } else {
530 WMSetApplicationIconImage(scr->wmscreen, image);
531 RReleaseImage(image);
536 *----------------------------------------------------------------------
537 * createInternalWindows--
538 * Creates some windows used internally by the program. One to
539 * receive input focus when no other window can get it and another
540 * to display window geometry information during window resize/move.
542 * Returns:
543 * Nothing
545 * Side effects:
546 * Windows are created and some colors are allocated for the
547 * window background.
548 *----------------------------------------------------------------------
550 static void createInternalWindows(WScreen * scr)
552 int vmask;
553 XSetWindowAttributes attribs;
555 /* InputOnly window to get the focus when no other window can get it */
556 vmask = CWEventMask | CWOverrideRedirect;
557 attribs.event_mask = KeyPressMask | FocusChangeMask;
558 attribs.override_redirect = True;
559 scr->no_focus_win = XCreateWindow(dpy, scr->root_win, -10, -10, 4, 4, 0, 0,
560 InputOnly, CopyFromParent, vmask, &attribs);
561 XSelectInput(dpy, scr->no_focus_win, KeyPressMask | KeyReleaseMask);
562 XMapWindow(dpy, scr->no_focus_win);
564 XSetInputFocus(dpy, scr->no_focus_win, RevertToParent, CurrentTime);
566 /* shadow window for dock buttons */
567 vmask = CWBorderPixel | CWBackPixmap | CWBackPixel | CWCursor | CWSaveUnder | CWOverrideRedirect;
568 attribs.border_pixel = scr->black_pixel;
569 attribs.save_under = True;
570 attribs.override_redirect = True;
571 attribs.background_pixmap = None;
572 attribs.background_pixel = scr->white_pixel;
573 attribs.cursor = wPreferences.cursor[WCUR_NORMAL];
574 vmask |= CWColormap;
575 attribs.colormap = scr->w_colormap;
576 scr->dock_shadow =
577 XCreateWindow(dpy, scr->root_win, 0, 0, wPreferences.icon_size,
578 wPreferences.icon_size, 0, scr->w_depth, CopyFromParent, scr->w_visual, vmask, &attribs);
580 /* workspace name */
581 vmask = CWBackPixel | CWSaveUnder | CWOverrideRedirect | CWColormap | CWBorderPixel;
582 attribs.save_under = True;
583 attribs.override_redirect = True;
584 attribs.colormap = scr->w_colormap;
585 attribs.background_pixel = scr->icon_back_texture->normal.pixel;
586 attribs.border_pixel = 0; /* do not care */
587 scr->workspace_name =
588 XCreateWindow(dpy, scr->root_win, 0, 0, 10, 10, 0, scr->w_depth,
589 CopyFromParent, scr->w_visual, vmask, &attribs);
590 scr->mini_screenshot_timeout = 0;
594 *----------------------------------------------------------------------
595 * wScreenInit--
596 * Initializes the window manager for the given screen and
597 * allocates a WScreen descriptor for it. Many resources are allocated
598 * for the screen and the root window is setup appropriately.
600 * Returns:
601 * The WScreen descriptor for the screen.
603 * Side effects:
604 * Many resources are allocated and the IconSize property is
605 * set on the root window.
606 * The program can be aborted if some fatal error occurs.
608 * TODO: User specifiable visual.
609 *----------------------------------------------------------------------
611 WScreen *wScreenInit(int screen_number)
613 WScreen *scr;
614 XIconSize icon_size[1];
615 RContextAttributes rattr = {};
616 long event_mask;
617 XErrorHandler oldHandler;
618 int i;
620 scr = wmalloc(sizeof(WScreen));
622 scr->stacking_list = WMCreateTreeBag();
624 /* initialize globals */
625 scr->screen = screen_number;
626 scr->root_win = RootWindow(dpy, screen_number);
627 scr->depth = DefaultDepth(dpy, screen_number);
628 scr->colormap = DefaultColormap(dpy, screen_number);
630 scr->scr_width = WidthOfScreen(ScreenOfDisplay(dpy, screen_number));
631 scr->scr_height = HeightOfScreen(ScreenOfDisplay(dpy, screen_number));
633 wInitXinerama(scr);
635 scr->usableArea = (WArea *) wmalloc(sizeof(WArea) * wXineramaHeads(scr));
636 scr->totalUsableArea = (WArea *) wmalloc(sizeof(WArea) * wXineramaHeads(scr));
638 for (i = 0; i < wXineramaHeads(scr); ++i) {
639 WMRect rect = wGetRectForHead(scr, i);
640 scr->usableArea[i].x1 = scr->totalUsableArea[i].x1 = rect.pos.x;
641 scr->usableArea[i].y1 = scr->totalUsableArea[i].y1 = rect.pos.y;
642 scr->usableArea[i].x2 = scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width;
643 scr->usableArea[i].y2 = scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
646 scr->fakeGroupLeaders = WMCreateArray(16);
648 CantManageScreen = 0;
649 oldHandler = XSetErrorHandler(alreadyRunningError);
651 /* Do we need to replace an existing window manager? */
652 if (!replace_existing_wm(scr)) {
653 XDestroyWindow(dpy, scr->info_window);
654 wfree(scr);
655 return NULL;
658 event_mask = EVENT_MASK;
659 XSelectInput(dpy, scr->root_win, event_mask);
661 #ifdef KEEP_XKB_LOCK_STATUS
662 /* Only GroupLock doesn't work correctly in my system since right-alt
663 * can change mode while holding it too - ]d
665 if (w_global.xext.xkb.supported) {
666 XkbSelectEvents(dpy, XkbUseCoreKbd, XkbStateNotifyMask, XkbStateNotifyMask);
668 #endif /* KEEP_XKB_LOCK_STATUS */
670 #ifdef USE_RANDR
671 if (w_global.xext.randr.supported)
672 XRRSelectInput(dpy, scr->root_win, RRScreenChangeNotifyMask);
673 #endif
675 XSync(dpy, False);
676 XSetErrorHandler(oldHandler);
678 if (CantManageScreen) {
679 wfree(scr);
680 return NULL;
683 XDefineCursor(dpy, scr->root_win, wPreferences.cursor[WCUR_ROOT]);
685 /* screen descriptor for raster graphic library */
686 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap;
687 rattr.render_mode = wPreferences.no_dithering ? RBestMatchRendering : RDitheredRendering;
689 /* if the std colormap stuff works ok, this will be ignored */
690 rattr.colors_per_channel = wPreferences.cmap_size;
691 if (rattr.colors_per_channel < 2)
692 rattr.colors_per_channel = 2;
694 /* Use standard colormap */
695 rattr.standard_colormap_mode = RUseStdColormap;
697 if (getWVisualID(screen_number) >= 0) {
698 rattr.flags |= RC_VisualID;
699 rattr.visualid = getWVisualID(screen_number);
702 scr->rcontext = RCreateContext(dpy, screen_number, &rattr);
704 if (!scr->rcontext && RErrorCode == RERR_STDCMAPFAIL) {
705 wwarning("%s", RMessageForError(RErrorCode));
707 rattr.flags &= ~RC_StandardColormap;
708 rattr.standard_colormap_mode = RUseStdColormap;
710 scr->rcontext = RCreateContext(dpy, screen_number, &rattr);
712 if (scr->rcontext == NULL) {
713 wfatal(_("can't create Context on screen %d, %s"),
714 screen_number, RMessageForError(RErrorCode));
715 goto abort_no_context;
718 scr->w_win = scr->rcontext->drawable;
719 scr->w_visual = scr->rcontext->visual;
720 scr->w_depth = scr->rcontext->depth;
721 scr->w_colormap = scr->rcontext->cmap;
723 /* create screen descriptor for WINGs */
724 scr->wmscreen = WMCreateScreenWithRContext(dpy, screen_number, scr->rcontext);
726 if (!scr->wmscreen) {
727 wfatal(_("could not initialize WINGs widget set"));
728 RDestroyContext(scr->rcontext);
729 abort_no_context:
730 WMFreeArray(scr->fakeGroupLeaders);
731 wfree(scr->totalUsableArea);
732 wfree(scr->usableArea);
733 WMFreeBag(scr->stacking_list);
734 wfree(scr);
735 return NULL;
738 scr->black = WMBlackColor(scr->wmscreen);
739 scr->white = WMWhiteColor(scr->wmscreen);
740 scr->gray = WMGrayColor(scr->wmscreen);
741 scr->darkGray = WMDarkGrayColor(scr->wmscreen);
743 scr->black_pixel = WMColorPixel(scr->black); /*scr->rcontext->black; */
744 scr->white_pixel = WMColorPixel(scr->white); /*scr->rcontext->white; */
745 scr->light_pixel = WMColorPixel(scr->gray);
746 scr->dark_pixel = WMColorPixel(scr->darkGray);
748 /* create GCs with default values */
749 allocGCs(scr);
751 /* read defaults for this screen */
752 wReadDefaults(scr, w_global.domain.wmaker->dictionary);
755 XColor xcol;
756 /* frame boder color */
757 wGetColor(scr, WMGetColorRGBDescription(scr->frame_border_color), &xcol);
758 scr->frame_border_pixel = xcol.pixel;
759 wGetColor(scr, WMGetColorRGBDescription(scr->frame_focused_border_color), &xcol);
760 scr->frame_focused_border_pixel = xcol.pixel;
761 wGetColor(scr, WMGetColorRGBDescription(scr->frame_selected_border_color), &xcol);
762 scr->frame_selected_border_pixel = xcol.pixel;
765 createInternalWindows(scr);
767 wNETWMInitStuff(scr);
769 /* create initial workspace */
770 wWorkspaceNew(scr);
772 /* create shared pixmaps */
773 createPixmaps(scr);
775 /* set icon sizes we can accept from clients */
776 icon_size[0].min_width = 8;
777 icon_size[0].min_height = 8;
778 icon_size[0].max_width = wPreferences.icon_size - 4;
779 icon_size[0].max_height = wPreferences.icon_size - 4;
780 icon_size[0].width_inc = 1;
781 icon_size[0].height_inc = 1;
782 XSetIconSizes(dpy, scr->root_win, icon_size, 1);
784 /* setup WindowMaker protocols property in the root window */
785 PropSetWMakerProtocols(scr->root_win);
787 /* setup our noticeboard */
788 XChangeProperty(dpy, scr->info_window, w_global.atom.wmaker.noticeboard,
789 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&scr->info_window, 1);
790 XChangeProperty(dpy, scr->root_win, w_global.atom.wmaker.noticeboard,
791 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&scr->info_window, 1);
793 #ifdef BALLOON_TEXT
794 /* initialize balloon text stuff */
795 wBalloonInitialize(scr);
796 #endif
798 scr->info_text_font = WMBoldSystemFontOfSize(scr->wmscreen, 12);
800 scr->tech_draw_font = XLoadQueryFont(dpy, "-adobe-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*");
801 if (!scr->tech_draw_font)
802 scr->tech_draw_font = XLoadQueryFont(dpy, "fixed");
804 scr->gview = WCreateGeometryView(scr->wmscreen);
805 WMRealizeWidget(scr->gview);
807 wScreenUpdateUsableArea(scr);
809 return scr;
812 void wScreenUpdateUsableArea(WScreen *scr)
815 * scr->totalUsableArea[] will become the usableArea used for Windowplacement,
816 * scr->usableArea[] will be used for iconplacement, hence no iconyard nor
817 * border.
820 WArea area;
821 int i;
822 unsigned long tmp_area, best_area = 0;
823 unsigned int size = wPreferences.workspace_border_size;
824 unsigned int position = wPreferences.workspace_border_position;
826 for (i = 0; i < wXineramaHeads(scr); ++i) {
827 WMRect rect = wGetRectForHead(scr, i);
828 scr->totalUsableArea[i].x1 = rect.pos.x;
829 scr->totalUsableArea[i].y1 = rect.pos.y;
830 scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width;
831 scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
833 if (wNETWMGetUsableArea(scr, i, &area)) {
834 scr->totalUsableArea[i].x1 = WMAX(scr->totalUsableArea[i].x1, area.x1);
835 scr->totalUsableArea[i].y1 = WMAX(scr->totalUsableArea[i].y1, area.y1);
836 scr->totalUsableArea[i].x2 = WMIN(scr->totalUsableArea[i].x2, area.x2);
837 scr->totalUsableArea[i].y2 = WMIN(scr->totalUsableArea[i].y2, area.y2);
840 scr->usableArea[i] = scr->totalUsableArea[i];
842 if (wPreferences.no_window_over_icons) {
843 if (wPreferences.icon_yard & IY_VERT) {
844 if (wPreferences.icon_yard & IY_RIGHT)
845 scr->totalUsableArea[i].x2 -= wPreferences.icon_size;
846 else
847 scr->totalUsableArea[i].x1 += wPreferences.icon_size;
848 } else {
849 if (wPreferences.icon_yard & IY_TOP)
850 scr->totalUsableArea[i].y1 += wPreferences.icon_size;
851 else
852 scr->totalUsableArea[i].y2 -= wPreferences.icon_size;
856 if (scr->totalUsableArea[i].x2 - scr->totalUsableArea[i].x1 < rect.size.width / 2) {
857 scr->totalUsableArea[i].x1 = rect.pos.x;
858 scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width;
861 if (scr->totalUsableArea[i].y2 - scr->totalUsableArea[i].y1 < rect.size.height / 2) {
862 scr->totalUsableArea[i].y1 = rect.pos.y;
863 scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
866 tmp_area = (scr->totalUsableArea[i].x2 - scr->totalUsableArea[i].x1) *
867 (scr->totalUsableArea[i].y2 - scr->totalUsableArea[i].y1);
869 if (tmp_area > best_area) {
870 best_area = tmp_area;
871 area = scr->totalUsableArea[i];
874 if (size > 0 && position != WB_NONE) {
875 if (position & WB_LEFTRIGHT) {
876 scr->totalUsableArea[i].x1 += size;
877 scr->totalUsableArea[i].x2 -= size;
879 if (position & WB_TOPBOTTOM) {
880 scr->totalUsableArea[i].y1 += size;
881 scr->totalUsableArea[i].y2 -= size;
886 if (wPreferences.auto_arrange_icons)
887 wArrangeIcons(scr, True);
890 void wScreenRestoreState(WScreen * scr)
892 WMPropList *state;
893 char *path;
895 OpenRootMenu(scr, -10000, -10000, False);
896 wMenuUnmap(scr->root_menu);
898 make_keys();
900 if (w_global.screen_count == 1) {
901 path = wdefaultspathfordomain("WMState");
902 } else {
903 char buf[16];
904 snprintf(buf, sizeof(buf), "WMState.%i", scr->screen);
905 path = wdefaultspathfordomain(buf);
907 scr->session_state = WMReadPropListFromFile(path);
908 wfree(path);
909 if (!scr->session_state && w_global.screen_count > 1) {
910 path = wdefaultspathfordomain("WMState");
911 scr->session_state = WMReadPropListFromFile(path);
912 wfree(path);
915 if (!scr->session_state)
916 scr->session_state = WMCreatePLDictionary(NULL, NULL);
918 if (!wPreferences.flags.nodock) {
919 state = WMGetFromPLDictionary(scr->session_state, dDock);
920 scr->dock = wDockRestoreState(scr, state, WM_DOCK);
923 if (!wPreferences.flags.noclip) {
924 state = WMGetFromPLDictionary(scr->session_state, dClip);
925 scr->clip_icon = wClipRestoreState(scr, state);
928 if (!wPreferences.flags.nodrawer) {
929 if (!scr->dock->on_right_side) {
930 /* Drawer tile was created early in wScreenInit() -> wReadDefaults(). At
931 * that time, scr->dock was NULL and the tile was created as if we were on
932 * the right side. If we aren't, redo it now. */
933 assert(scr->drawer_tile);
934 RReleaseImage(scr->drawer_tile);
935 scr->drawer_tile = wDrawerMakeTile(scr, scr->icon_tile);
937 wDrawersRestoreState(scr);
940 wWorkspaceRestoreState(scr);
941 wScreenUpdateUsableArea(scr);
944 void wScreenSaveState(WScreen * scr)
946 WWindow *wwin;
947 char *str;
948 WMPropList *old_state, *foo;
950 make_keys();
952 /* save state of windows */
953 wwin = scr->focused_window;
954 while (wwin) {
955 wWindowSaveState(wwin);
956 wwin = wwin->prev;
959 if (wPreferences.flags.noupdates)
960 return;
962 old_state = scr->session_state;
963 scr->session_state = WMCreatePLDictionary(NULL, NULL);
965 WMPLSetCaseSensitive(True);
967 /* save dock state to file */
968 if (!wPreferences.flags.nodock) {
969 wDockSaveState(scr, old_state);
970 } else {
971 foo = WMGetFromPLDictionary(old_state, dDock);
972 if (foo != NULL)
973 WMPutInPLDictionary(scr->session_state, dDock, foo);
975 if (!wPreferences.flags.noclip) {
976 wClipSaveState(scr);
977 } else {
978 foo = WMGetFromPLDictionary(old_state, dClip);
979 if (foo != NULL)
980 WMPutInPLDictionary(scr->session_state, dClip, foo);
983 wWorkspaceSaveState(scr, old_state);
985 if (!wPreferences.flags.nodrawer) {
986 wDrawersSaveState(scr);
987 } else {
988 foo = WMGetFromPLDictionary(old_state, dDrawers);
989 if (foo != NULL)
990 WMPutInPLDictionary(scr->session_state, dDrawers, foo);
994 if (wPreferences.save_session_on_exit) {
995 wSessionSaveState(scr);
996 } else {
997 foo = WMGetFromPLDictionary(old_state, dApplications);
998 if (foo != NULL)
999 WMPutInPLDictionary(scr->session_state, dApplications, foo);
1001 foo = WMGetFromPLDictionary(old_state, dWorkspace);
1002 if (foo != NULL)
1003 WMPutInPLDictionary(scr->session_state, dWorkspace, foo);
1006 /* clean up */
1007 WMPLSetCaseSensitive(False);
1009 wMenuSaveState(scr);
1011 if (w_global.screen_count == 1) {
1012 str = wdefaultspathfordomain("WMState");
1013 } else {
1014 char buf[16];
1015 snprintf(buf, sizeof(buf), "WMState.%i", scr->screen);
1016 str = wdefaultspathfordomain(buf);
1018 if (!WMWritePropListToFile(scr->session_state, str)) {
1019 werror(_("could not save session state in %s"), str);
1021 wfree(str);
1022 WMReleasePropList(old_state);
1025 int wScreenBringInside(WScreen * scr, int *x, int *y, int width, int height)
1027 int moved = 0;
1028 int tol_w, tol_h;
1030 * With respect to the head that contains most of the window.
1032 int sx1, sy1, sx2, sy2;
1034 WMRect rect;
1035 int head, flags;
1037 rect.pos.x = *x;
1038 rect.pos.y = *y;
1039 rect.size.width = width;
1040 rect.size.height = height;
1042 head = wGetRectPlacementInfo(scr, rect, &flags);
1043 rect = wGetRectForHead(scr, head);
1045 sx1 = rect.pos.x;
1046 sy1 = rect.pos.y;
1047 sx2 = sx1 + rect.size.width;
1048 sy2 = sy1 + rect.size.height;
1050 #if 0 /* NOTE: gives funky group movement */
1051 if (flags & XFLAG_MULTIPLE) {
1053 * since we span multiple heads, pull window totaly inside
1055 if (*x < sx1)
1056 *x = sx1, moved = 1;
1057 else if (*x + width > sx2)
1058 *x = sx2 - width, moved = 1;
1060 if (*y < sy1)
1061 *y = sy1, moved = 1;
1062 else if (*y + height > sy2)
1063 *y = sy2 - height, moved = 1;
1065 return moved;
1067 #endif
1069 if (width > 20)
1070 tol_w = width / 2;
1071 else
1072 tol_w = 20;
1074 if (height > 20)
1075 tol_h = height / 2;
1076 else
1077 tol_h = 20;
1079 if (*x + width < sx1 + 10)
1080 *x = sx1 - tol_w, moved = 1;
1081 else if (*x >= sx2 - 10)
1082 *x = sx2 - tol_w - 1, moved = 1;
1084 if (*y < sy1 - height + 10)
1085 *y = sy1 - tol_h, moved = 1;
1086 else if (*y >= sy2 - 10)
1087 *y = sy2 - tol_h - 1, moved = 1;
1089 return moved;
1092 int wScreenKeepInside(WScreen * scr, int *x, int *y, int width, int height)
1094 int moved = 0;
1095 int sx1, sy1, sx2, sy2;
1096 WMRect rect;
1097 int head;
1099 rect.pos.x = *x;
1100 rect.pos.y = *y;
1101 rect.size.width = width;
1102 rect.size.height = height;
1104 head = wGetHeadForRect(scr, rect);
1105 rect = wGetRectForHead(scr, head);
1107 sx1 = rect.pos.x;
1108 sy1 = rect.pos.y;
1109 sx2 = sx1 + rect.size.width;
1110 sy2 = sy1 + rect.size.height;
1112 if (*x < sx1)
1113 *x = sx1, moved = 1;
1114 else if (*x + width > sx2)
1115 *x = sx2 - width, moved = 1;
1117 if (*y < sy1)
1118 *y = sy1, moved = 1;
1119 else if (*y + height > sy2)
1120 *y = sy2 - height, moved = 1;
1122 return moved;
1125 static XImage *imageCaptureArea(WScreen *scr)
1127 XEvent event;
1128 int quit = 0;
1129 int xp = -1;
1130 int yp = -1;
1131 int w = 0, h = 0;
1132 int x = xp, y = yp;
1134 if (XGrabPointer(dpy, scr->root_win, False, ButtonMotionMask
1135 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
1136 GrabModeAsync, None, wPreferences.cursor[WCUR_CAPTURE], CurrentTime) != Success) {
1137 return NULL;
1140 XGrabServer(dpy);
1142 while (!quit) {
1143 WMMaskEvent(dpy, ButtonReleaseMask | PointerMotionMask | ButtonPressMask | KeyPressMask, &event);
1145 switch (event.type) {
1146 case ButtonPress:
1147 if (event.xbutton.button == Button1) {
1148 xp = event.xbutton.x_root;
1149 yp = event.xbutton.y_root;
1151 break;
1152 case ButtonRelease:
1153 if (event.xbutton.button == Button1) {
1154 quit = 1;
1155 if (w > 0 && h > 0) {
1156 XDrawRectangle(dpy, scr->root_win, scr->frame_gc, x, y, w, h);
1157 XUngrabServer(dpy);
1158 XUngrabPointer(dpy, CurrentTime);
1159 return XGetImage(dpy, scr->root_win, x, y, w, h, AllPlanes, ZPixmap);
1162 break;
1163 case MotionNotify:
1164 XDrawRectangle(dpy, scr->root_win, scr->frame_gc, x, y, w, h);
1165 x = event.xmotion.x_root;
1166 if (x < xp) {
1167 w = xp - x;
1168 } else {
1169 w = x - xp;
1170 x = xp;
1172 y = event.xmotion.y_root;
1173 if (y < yp) {
1174 h = yp - y;
1175 } else {
1176 h = y - yp;
1177 y = yp;
1179 XDrawRectangle(dpy, scr->root_win, scr->frame_gc, x, y, w, h);
1180 break;
1181 case KeyPress:
1182 if (W_KeycodeToKeysym(dpy, event.xkey.keycode, 0) == XK_Escape)
1183 quit = 1;
1184 break;
1185 default:
1186 WMHandleEvent(&event);
1187 break;
1191 XUngrabServer(dpy);
1192 XUngrabPointer(dpy, CurrentTime);
1193 return NULL;
1196 static void hideMiniScreenshot(void *data)
1198 WScreen *scr = (WScreen *) data;
1200 if (time(NULL) < scr->mini_screenshot_timeout) {
1201 scr->mini_screenshot_timer = WMAddTimerHandler(WORKSPACE_NAME_FADE_DELAY, hideMiniScreenshot, scr);
1202 } else {
1203 XWindowAttributes attr;
1205 WMDeleteTimerHandler(scr->mini_screenshot_timer);
1206 if (XGetWindowAttributes(dpy, scr->mini_screenshot, &attr))
1207 slide_window(scr->mini_screenshot, attr.x, attr.y, attr.x + attr.width, attr.y);
1208 XUnmapWindow(dpy, scr->mini_screenshot);
1209 XDestroyWindow(dpy, scr->mini_screenshot);
1210 scr->mini_screenshot_timeout = 0;
1214 static void showMiniScreenshot(WScreen *scr, RImage *img)
1216 Pixmap pix;
1217 int x = scr->scr_width - img->width - 20;
1218 int y = scr->scr_height - img->height - 20;
1220 if (!scr->mini_screenshot_timeout) {
1221 Window win;
1223 win = XCreateSimpleWindow(dpy, scr->root_win, x, y,
1224 img->width, img->height, scr->frame_border_width, 0, 0);
1225 scr->mini_screenshot = win;
1227 RConvertImage(scr->rcontext, img, &pix);
1228 XMapWindow(dpy, scr->mini_screenshot);
1229 XCopyArea(dpy, pix, scr->mini_screenshot, scr->rcontext->copy_gc, 0, 0, img->width, img->height, 0, 0);
1230 XFlush(dpy);
1232 scr->mini_screenshot_timeout = time(NULL) + 2;
1233 scr->mini_screenshot_timer = WMAddTimerHandler(WORKSPACE_NAME_FADE_DELAY, hideMiniScreenshot, scr);
1236 void ScreenCapture(WScreen *scr, int mode)
1238 time_t s;
1239 short i = 0;
1240 struct tm *tm_info;
1241 char index_str[12] = "";
1242 char filename_date_part[60];
1243 char filename[60];
1244 char *filepath;
1245 char *screenshot_dir;
1246 RImage *img = NULL;
1247 RImage *scale_img = NULL;
1249 #ifdef USE_PNG
1250 char *filetype = ".png";
1251 #else
1252 #ifdef USE_JPEG
1253 char *filetype = ".jpg";
1254 #else
1255 char *filetype = NULL;
1256 #endif
1257 #endif
1259 if (!filetype) {
1260 werror(_("Unable to find a proper screenshot image format"));
1261 return;
1264 screenshot_dir = wstrconcat(wusergnusteppath(), "/Library/WindowMaker/Screenshots/");
1265 if (-1 == mkdir(screenshot_dir, 0700) && errno != EEXIST) {
1266 wfree(screenshot_dir);
1267 werror(_("Unable to create screenshot directory: %s"), strerror(errno));
1268 return;
1271 s = time(NULL);
1272 tm_info = localtime(&s);
1273 strftime(filename_date_part, sizeof(filename_date_part), "screenshot_%Y-%m-%d_at_%H:%M:%S", tm_info);
1274 strcpy(filename, filename_date_part);
1276 filepath = wstrconcat(screenshot_dir, strcat(filename, filetype));
1277 while (access(filepath, F_OK) == 0 && i < 600) {
1278 i++;
1279 strcpy(filename, filename_date_part);
1280 sprintf(index_str, "_%d", i);
1281 strncat(filename, index_str, sizeof(filename) - strlen(filename) - 1);
1282 wfree(filepath);
1283 filepath = wstrconcat(screenshot_dir, strcat(filename, filetype));
1286 /* cannot generate an available filename ?! */
1287 if (i == 600) {
1288 wfree(filepath);
1289 wfree(screenshot_dir);
1290 werror(_("Could not generate a free screenshot filename"));
1291 return;
1294 switch (mode) {
1295 WWindow *wwin;
1296 XImage *pimg;
1298 case PRINT_WINDOW:
1299 wwin = scr->focused_window;
1300 if (wwin && !wwin->flags.shaded) {
1302 * check if hint WM_TAKE_FOCUS is set, if it's the case
1303 * we can take screenshot of the out of screen window
1305 if (wwin->focus_mode >= WFM_LOCALLY_ACTIVE) {
1306 img = RCreateImageFromDrawable(scr->rcontext, wwin->client_win, None);
1308 else {
1309 /* we will only capture the visible window part */
1310 int x_crop = 0;
1311 int y_crop = 0;
1312 int w_crop = wwin->client.width;
1313 int h_crop = wwin->client.height;
1315 if (wwin->client.x > 0)
1316 x_crop = wwin->client.x;
1317 if (wwin->client.y > 0)
1318 y_crop = wwin->client.y;
1320 if (wwin->client.x + wwin->client.width > scr->scr_width)
1321 w_crop = scr->scr_width - wwin->client.x;
1322 if (wwin->client.y + wwin->client.height > scr->scr_height)
1323 h_crop = scr->scr_height - wwin->client.y;
1325 pimg = XGetImage(dpy, scr->root_win, x_crop, y_crop,
1326 (wwin->client.x > 0)?w_crop:w_crop + wwin->client.x,
1327 (wwin->client.y > 0)?h_crop:h_crop + wwin->client.y,
1328 AllPlanes, ZPixmap);
1330 if (pimg) {
1331 img = RCreateImageFromXImage(scr->rcontext, pimg, None);
1332 XDestroyImage(pimg);
1336 break;
1337 case PRINT_PARTIAL:
1338 pimg = imageCaptureArea(scr);
1339 if (pimg) {
1340 img = RCreateImageFromXImage(scr->rcontext, pimg, None);
1341 XDestroyImage(pimg);
1343 break;
1344 default:
1345 /* PRINT_SCREEN*/
1346 img = RCreateImageFromDrawable(scr->rcontext, scr->root_win, None);
1349 if (img) {
1350 #ifdef USE_PNG
1351 if (RSaveTitledImage(img, filepath, (char *)(filetype + 1), "Screenshot from Window Maker")) {
1352 #else
1353 if (RSaveTitledImage(img, filepath, (char *)(filetype + 1), "Screenshot from Window Maker")) {
1354 #endif
1355 scale_img = RSmoothScaleImage(img, scr->scr_width / 10, scr->scr_height / 10);
1356 showMiniScreenshot(scr, scale_img);
1357 RReleaseImage(scale_img);
1358 #ifdef DEBUG
1359 wmessage("screenshot filepath: %s", filepath);
1360 #endif
1362 RReleaseImage(img);
1364 wfree(filepath);
1365 wfree(screenshot_dir);