/* screen.c - screen management * * Window Maker window manager * * Copyright (c) 1997-2003 Alfredo K. Kojima * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "wconfig.h" #include #include #include #include #include #include #include #include #include "WindowMaker.h" #include "def_pixmaps.h" #include "screen.h" #include "texture.h" #include "pixmap.h" #include "menu.h" #include "window.h" #include "main.h" #include "actions.h" #include "properties.h" #include "dock.h" #include "resources.h" #include "workspace.h" #include "session.h" #include "balloon.h" #include "geomview.h" #include "wmspec.h" #include "rootmenu.h" #include "misc.h" #include "xinerama.h" #include "backend/backend.h" #include #include #include "defaults.h" #define EVENT_MASK (LeaveWindowMask|EnterWindowMask|PropertyChangeMask\ |SubstructureNotifyMask|PointerMotionMask \ |SubstructureRedirectMask|ButtonPressMask|ButtonReleaseMask\ |KeyPressMask|KeyReleaseMask) static WMPropList *dApplications = NULL; static WMPropList *dWorkspace; static WMPropList *dDock; static WMPropList *dClip; static WMPropList *dDrawers = NULL; static void make_keys(void) { if (dApplications != NULL) return; dApplications = WMCreatePLString("Applications"); dWorkspace = WMCreatePLString("Workspace"); dDock = WMCreatePLString("Dock"); dClip = WMCreatePLString("Clip"); dDrawers = WMCreatePLString("Drawers"); } void create_logo_image(WScreen *scr) { RImage *image = get_icon_image(scr, "Logo", "WMPanel", 128); if (!image) { wwarning(_("could not load logo image for panels: %s"), RMessageForError(RErrorCode)); } else { WMSetApplicationIconImage(scr->wmscreen, image); RReleaseImage(image); } } /* *---------------------------------------------------------------------- * wScreenInit-- * Initializes the window manager for the given screen and * allocates a WScreen descriptor for it. Many resources are allocated * for the screen and the root window is setup appropriately. * * Returns: * The WScreen descriptor for the screen. * * Side effects: * Many resources are allocated and the IconSize property is * set on the root window. * The program can be aborted if some fatal error occurs. * * TODO: User specifiable visual. *---------------------------------------------------------------------- */ WScreen *wScreenInit(int screen_number) { WScreen *scr; RContextAttributes rattr = {}; int i; scr = wmalloc(sizeof(WScreen)); scr->stacking_list = WMCreateTreeBag(); scr->screen = screen_number; /* Allocate backend per-screen state; fills root_win, depth, colormap, * scr_width, scr_height as well. */ wm_backend->screen_open(scr); wInitXinerama(scr); scr->usableArea = (WArea *) wmalloc(sizeof(WArea) * wXineramaHeads(scr)); scr->totalUsableArea = (WArea *) wmalloc(sizeof(WArea) * wXineramaHeads(scr)); for (i = 0; i < wXineramaHeads(scr); ++i) { WMRect rect = wGetRectForHead(scr, i); scr->usableArea[i].x1 = scr->totalUsableArea[i].x1 = rect.pos.x; scr->usableArea[i].y1 = scr->totalUsableArea[i].y1 = rect.pos.y; scr->usableArea[i].x2 = scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width; scr->usableArea[i].y2 = scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height; } scr->fakeGroupLeaders = WMCreateArray(16); if (!wm_backend->screen_replace_wm(scr)) { wfree(scr); return NULL; } if (!wm_backend->screen_select_input(scr, EVENT_MASK)) { wfree(scr); return NULL; } wm_backend->monitors_select_events(scr); wm_backend->screen_set_root_cursor(scr, WCUR_ROOT); /* Wayland path: screen_open() sets geometry; X11/wrlib context not needed. * Allocate workspace 0 directly (wWorkspaceNew is unsafe here because it * calls wDockCreate which requires X11 rendering resources). */ if (!dpy) { WWorkspace *wspace = wmalloc(sizeof(WWorkspace)); wspace->name = wstrdup("Workspace 1"); wspace->clip = NULL; wspace->map = NULL; scr->workspaces = wmalloc(sizeof(WWorkspace *)); scr->workspaces[0] = wspace; scr->workspace_count = 1; /* Phase 62: run backend screen internals (sets no_focus_win, * auto_focus, clears keyboard focus) before leaving the Wayland * fast path — screen_create_internals is normally called at * line 248 which the goto would skip. */ wm_backend->screen_create_internals(scr); goto screen_done; } /* screen descriptor for raster graphic library */ rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap; rattr.render_mode = wPreferences.no_dithering ? RBestMatchRendering : RDitheredRendering; /* if the std colormap stuff works ok, this will be ignored */ rattr.colors_per_channel = wPreferences.cmap_size; if (rattr.colors_per_channel < 2) rattr.colors_per_channel = 2; /* Use standard colormap */ rattr.standard_colormap_mode = RUseStdColormap; if (getWVisualID(screen_number) >= 0) { rattr.flags |= RC_VisualID; rattr.visualid = getWVisualID(screen_number); } scr->rcontext = RCreateContext(dpy, screen_number, &rattr); if (!scr->rcontext && RErrorCode == RERR_STDCMAPFAIL) { wwarning("%s", RMessageForError(RErrorCode)); rattr.flags &= ~RC_StandardColormap; rattr.standard_colormap_mode = RUseStdColormap; scr->rcontext = RCreateContext(dpy, screen_number, &rattr); } if (scr->rcontext == NULL) { wfatal(_("can't create Context on screen %d, %s"), screen_number, RMessageForError(RErrorCode)); goto abort_no_context; } scr->w_win = scr->rcontext->drawable; scr->w_visual = scr->rcontext->visual; scr->w_depth = scr->rcontext->depth; scr->w_colormap = scr->rcontext->cmap; /* create screen descriptor for WINGs */ scr->wmscreen = WMCreateScreenWithRContext(dpy, screen_number, scr->rcontext); if (!scr->wmscreen) { wfatal(_("could not initialize WINGs widget set")); RDestroyContext(scr->rcontext); abort_no_context: WMFreeArray(scr->fakeGroupLeaders); wfree(scr->totalUsableArea); wfree(scr->usableArea); WMFreeBag(scr->stacking_list); wfree(scr); return NULL; } scr->black = WMBlackColor(scr->wmscreen); scr->white = WMWhiteColor(scr->wmscreen); scr->gray = WMGrayColor(scr->wmscreen); scr->darkGray = WMDarkGrayColor(scr->wmscreen); scr->black_pixel = WMColorPixel(scr->black); scr->white_pixel = WMColorPixel(scr->white); scr->light_pixel = WMColorPixel(scr->gray); scr->dark_pixel = WMColorPixel(scr->darkGray); /* create GCs with default values */ wm_backend->screen_alloc_gcs(scr); /* read defaults for this screen */ wReadDefaults(scr, w_global.domain.wmaker->dictionary); scr->frame_border_pixel = WMColorPixel(scr->frame_border_color); scr->frame_focused_border_pixel = WMColorPixel(scr->frame_focused_border_color); scr->frame_selected_border_pixel = WMColorPixel(scr->frame_selected_border_color); wm_backend->screen_create_internals(scr); wNETWMInitStuff(scr); /* create initial workspace */ wWorkspaceNew(scr); /* create shared pixmaps */ wm_backend->screen_create_pixmaps(scr); create_logo_image(scr); /* set icon sizes we can accept from clients */ wm_backend->screen_set_icon_sizes(scr, 8, wPreferences.icon_size - 4); /* setup WindowMaker protocols property in the root window */ PropSetWMakerProtocols(scr->root_win); /* setup our noticeboard */ wm_backend->screen_setup_noticeboard(scr); #ifdef BALLOON_TEXT /* initialize balloon text stuff */ wBalloonInitialize(scr); #endif scr->info_text_font = WMBoldSystemFontOfSize(scr->wmscreen, 12); wm_backend->screen_load_tech_font(scr); scr->gview = WCreateGeometryView(scr->wmscreen); WMRealizeWidget(scr->gview); wScreenUpdateUsableArea(scr); screen_done: return scr; } void wScreenUpdateUsableArea(WScreen *scr) { /* * scr->totalUsableArea[] will become the usableArea used for Windowplacement, * scr->usableArea[] will be used for iconplacement, hence no iconyard nor * border. */ WArea area; int i; unsigned long tmp_area, best_area = 0; unsigned int size = wPreferences.workspace_border_size; unsigned int position = wPreferences.workspace_border_position; for (i = 0; i < wXineramaHeads(scr); ++i) { WMRect rect = wGetRectForHead(scr, i); scr->totalUsableArea[i].x1 = rect.pos.x; scr->totalUsableArea[i].y1 = rect.pos.y; scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width; scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height; if (wNETWMGetUsableArea(scr, i, &area)) { scr->totalUsableArea[i].x1 = WMAX(scr->totalUsableArea[i].x1, area.x1); scr->totalUsableArea[i].y1 = WMAX(scr->totalUsableArea[i].y1, area.y1); scr->totalUsableArea[i].x2 = WMIN(scr->totalUsableArea[i].x2, area.x2); scr->totalUsableArea[i].y2 = WMIN(scr->totalUsableArea[i].y2, area.y2); } scr->usableArea[i] = scr->totalUsableArea[i]; if (wPreferences.no_window_over_icons) { if (wPreferences.icon_yard & IY_VERT) { if (wPreferences.icon_yard & IY_RIGHT) scr->totalUsableArea[i].x2 -= wPreferences.icon_size; else scr->totalUsableArea[i].x1 += wPreferences.icon_size; } else { if (wPreferences.icon_yard & IY_TOP) scr->totalUsableArea[i].y1 += wPreferences.icon_size; else scr->totalUsableArea[i].y2 -= wPreferences.icon_size; } } if (scr->totalUsableArea[i].x2 - scr->totalUsableArea[i].x1 < rect.size.width / 2) { scr->totalUsableArea[i].x1 = rect.pos.x; scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width; } if (scr->totalUsableArea[i].y2 - scr->totalUsableArea[i].y1 < rect.size.height / 2) { scr->totalUsableArea[i].y1 = rect.pos.y; scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height; } tmp_area = (scr->totalUsableArea[i].x2 - scr->totalUsableArea[i].x1) * (scr->totalUsableArea[i].y2 - scr->totalUsableArea[i].y1); if (tmp_area > best_area) { best_area = tmp_area; area = scr->totalUsableArea[i]; } if (size > 0 && position != WB_NONE) { if (position & WB_LEFTRIGHT) { scr->totalUsableArea[i].x1 += size; scr->totalUsableArea[i].x2 -= size; } if (position & WB_TOPBOTTOM) { scr->totalUsableArea[i].y1 += size; scr->totalUsableArea[i].y2 -= size; } } } if (wPreferences.auto_arrange_icons) wArrangeIcons(scr, True); } void wScreenRestoreState(WScreen * scr) { WMPropList *state; char *path; OpenRootMenu(scr, -10000, -10000, False); wMenuUnmap(scr->root_menu); make_keys(); if (w_global.screen_count == 1) { path = wdefaultspathfordomain("WMState"); } else { char buf[16]; snprintf(buf, sizeof(buf), "WMState.%i", scr->screen); path = wdefaultspathfordomain(buf); } scr->session_state = WMReadPropListFromFile(path); wfree(path); if (!scr->session_state && w_global.screen_count > 1) { path = wdefaultspathfordomain("WMState"); scr->session_state = WMReadPropListFromFile(path); wfree(path); } if (!scr->session_state) scr->session_state = WMCreatePLDictionary(NULL, NULL); if (!wPreferences.flags.nodock) { state = WMGetFromPLDictionary(scr->session_state, dDock); scr->dock = wDockRestoreState(scr, state, WM_DOCK); } if (!wPreferences.flags.noclip) { state = WMGetFromPLDictionary(scr->session_state, dClip); scr->clip_icon = wClipRestoreState(scr, state); } if (!wPreferences.flags.nodrawer) { if (!scr->dock->on_right_side) { /* Drawer tile was created early in wScreenInit() -> wReadDefaults(). At * that time, scr->dock was NULL and the tile was created as if we were on * the right side. If we aren't, redo it now. */ assert(scr->drawer_tile); RReleaseImage(scr->drawer_tile); scr->drawer_tile = wDrawerMakeTile(scr, scr->icon_tile); } wDrawersRestoreState(scr); } wWorkspaceRestoreState(scr); wScreenUpdateUsableArea(scr); } void wScreenSaveState(WScreen * scr) { WWindow *wwin; char *str; WMPropList *old_state, *foo; make_keys(); /* save state of windows */ wwin = scr->focused_window; while (wwin) { wWindowSaveState(wwin); wwin = wwin->prev; } if (wPreferences.flags.noupdates) return; old_state = scr->session_state; scr->session_state = WMCreatePLDictionary(NULL, NULL); WMPLSetCaseSensitive(True); /* save dock state to file */ if (!wPreferences.flags.nodock) { wDockSaveState(scr, old_state); } else { foo = WMGetFromPLDictionary(old_state, dDock); if (foo != NULL) WMPutInPLDictionary(scr->session_state, dDock, foo); } if (!wPreferences.flags.noclip) { wClipSaveState(scr); } else { foo = WMGetFromPLDictionary(old_state, dClip); if (foo != NULL) WMPutInPLDictionary(scr->session_state, dClip, foo); } wWorkspaceSaveState(scr, old_state); if (!wPreferences.flags.nodrawer) { wDrawersSaveState(scr); } else { foo = WMGetFromPLDictionary(old_state, dDrawers); if (foo != NULL) WMPutInPLDictionary(scr->session_state, dDrawers, foo); } if (wPreferences.save_session_on_exit) { wSessionSaveState(scr); } else { foo = WMGetFromPLDictionary(old_state, dApplications); if (foo != NULL) WMPutInPLDictionary(scr->session_state, dApplications, foo); foo = WMGetFromPLDictionary(old_state, dWorkspace); if (foo != NULL) WMPutInPLDictionary(scr->session_state, dWorkspace, foo); } /* clean up */ WMPLSetCaseSensitive(False); wMenuSaveState(scr); if (w_global.screen_count == 1) { str = wdefaultspathfordomain("WMState"); } else { char buf[16]; snprintf(buf, sizeof(buf), "WMState.%i", scr->screen); str = wdefaultspathfordomain(buf); } if (!WMWritePropListToFile(scr->session_state, str)) { werror(_("could not save session state in %s"), str); } wfree(str); WMReleasePropList(old_state); } int wScreenBringInside(WScreen * scr, int *x, int *y, int width, int height) { int moved = 0; int tol_w, tol_h; /* * With respect to the head that contains most of the window. */ int sx1, sy1, sx2, sy2; WMRect rect; int head, flags; rect.pos.x = *x; rect.pos.y = *y; rect.size.width = width; rect.size.height = height; head = wGetRectPlacementInfo(scr, rect, &flags); rect = wGetRectForHead(scr, head); sx1 = rect.pos.x; sy1 = rect.pos.y; sx2 = sx1 + rect.size.width; sy2 = sy1 + rect.size.height; #if 0 /* NOTE: gives funky group movement */ if (flags & XFLAG_MULTIPLE) { /* * since we span multiple heads, pull window totaly inside */ if (*x < sx1) *x = sx1, moved = 1; else if (*x + width > sx2) *x = sx2 - width, moved = 1; if (*y < sy1) *y = sy1, moved = 1; else if (*y + height > sy2) *y = sy2 - height, moved = 1; return moved; } #endif if (width > 20) tol_w = width / 2; else tol_w = 20; if (height > 20) tol_h = height / 2; else tol_h = 20; if (*x + width < sx1 + 10) *x = sx1 - tol_w, moved = 1; else if (*x >= sx2 - 10) *x = sx2 - tol_w - 1, moved = 1; if (*y < sy1 - height + 10) *y = sy1 - tol_h, moved = 1; else if (*y >= sy2 - 10) *y = sy2 - tol_h - 1, moved = 1; return moved; } int wScreenKeepInside(WScreen * scr, int *x, int *y, int width, int height) { int moved = 0; int sx1, sy1, sx2, sy2; WMRect rect; int head; rect.pos.x = *x; rect.pos.y = *y; rect.size.width = width; rect.size.height = height; head = wGetHeadForRect(scr, rect); rect = wGetRectForHead(scr, head); sx1 = rect.pos.x; sy1 = rect.pos.y; sx2 = sx1 + rect.size.width; sy2 = sy1 + rect.size.height; if (*x < sx1) *x = sx1, moved = 1; else if (*x + width > sx2) *x = sx2 - width, moved = 1; if (*y < sy1) *y = sy1, moved = 1; else if (*y + height > sy2) *y = sy2 - height, moved = 1; return moved; } void ScreenCapture(WScreen *scr, int mode) { time_t s; short i = 0; struct tm *tm_info; char index_str[12] = ""; char filename_date_part[60]; char filename[60]; char *filepath; char *screenshot_dir; RImage *img = NULL; RImage *scale_img = NULL; #ifdef USE_PNG char *filetype = ".png"; #else #ifdef USE_JPEG char *filetype = ".jpg"; #else char *filetype = NULL; #endif #endif if (!filetype) { werror(_("Unable to find a proper screenshot image format")); return; } screenshot_dir = wstrconcat(wusergnusteppath(), "/Library/WindowMaker/Screenshots/"); if (-1 == mkdir(screenshot_dir, 0700) && errno != EEXIST) { wfree(screenshot_dir); werror(_("Unable to create screenshot directory: %s"), strerror(errno)); return; } s = time(NULL); tm_info = localtime(&s); strftime(filename_date_part, sizeof(filename_date_part), "screenshot_%Y-%m-%d_at_%H:%M:%S", tm_info); strcpy(filename, filename_date_part); filepath = wstrconcat(screenshot_dir, strcat(filename, filetype)); while (access(filepath, F_OK) == 0 && i < 600) { i++; strcpy(filename, filename_date_part); sprintf(index_str, "_%d", i); strncat(filename, index_str, sizeof(filename) - strlen(filename) - 1); wfree(filepath); filepath = wstrconcat(screenshot_dir, strcat(filename, filetype)); } /* cannot generate an available filename ?! */ if (i == 600) { wfree(filepath); wfree(screenshot_dir); werror(_("Could not generate a free screenshot filename")); return; } switch (mode) { WWindow *wwin; case PRINT_WINDOW: wwin = scr->focused_window; if (wwin && !wwin->flags.shaded) { if (wwin->focus_mode >= WFM_LOCALLY_ACTIVE) { img = RCreateImageFromDrawable(scr->rcontext, wwin->client_win, None); } else { img = wm_backend->screen_capture_window(scr, wwin); } } break; case PRINT_PARTIAL: img = wm_backend->screen_capture_area(scr); break; default: /* PRINT_SCREEN */ img = RCreateImageFromDrawable(scr->rcontext, scr->root_win, None); } if (img) { if (RSaveTitledImage(img, filepath, (char *)(filetype + 1), "Screenshot from Window Maker")) { scale_img = RSmoothScaleImage(img, scr->scr_width / 10, scr->scr_height / 10); wm_backend->screen_show_mini_screenshot(scr, scale_img); RReleaseImage(scale_img); #ifdef DEBUG wmessage("screenshot filepath: %s", filepath); #endif } RReleaseImage(img); } wfree(filepath); wfree(screenshot_dir); }