/* Screen.cc Copyright © 2003 David Reveman. This file is part of Waimea. Waimea 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, or (at your option) any later version. Waimea 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 Waimea; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H # include "../config.h" #endif // HAVE_CONFIG_H extern "C" { #include #include #include #ifdef RENDER # include #endif // RENDER #ifdef RANDR # include #endif // RANDR #ifdef HAVE_STDIO_H # include #endif // HAVE_STDIO_H #ifdef HAVE_UNISTD_H # include # include # include #endif // HAVE_UNISTD_H #ifdef STDC_HEADERS # include #endif // STDC_HEADERS #ifdef HAVE_SIGNAL_H # include #endif // HAVE_SIGNAL_H #ifdef HAVE_ERRNO_H # include #endif // HAVE_ERRNO_H } #include "Screen.hh" #include "Style.hh" #include "Window.hh" WaScreen::WaScreen(Display *d, int scrn_number, Waimea *wa) : RootWindowObject(NULL, 0, RootType, NULL, "root") { Window ro, pa, *children; int eventmask, dummy; unsigned int nchild, i; XSetWindowAttributes attrib_set; ws = this; display = d; screen_number = scrn_number; id = RootWindow(display, screen_number); visual = DefaultVisual(display, screen_number); colormap = DefaultColormap(display, screen_number); XGetGeometry(display, id, &ro, &dummy, &dummy, &width, &height, &i, &screen_depth); vdpi = ((double) height * 25.4) / (double) DisplayHeightMM(display, screen_number); hdpi = ((double) width * 25.4) / (double) DisplayWidthMM(display, screen_number); waimea = wa; net = waimea->net; rh = wa->rh; focused = true; shutdown = dont_propagate_cfg_update = false; bg_surface = NULL; eventmask = StructureNotifyMask | SubstructureRedirectMask | PropertyChangeMask | ColormapChangeMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask | StructureNotifyMask; i = snprintf(displaystring, 256, "DISPLAY=%s", DisplayString(display)); if (i < 256) snprintf(displaystring + i - 1, 256 - i, "%d", screen_number); XSetErrorHandler((XErrorHandler) wmrunningerror); XSelectInput(display, id, eventmask); XSync(display, false); XSetErrorHandler((XErrorHandler) xerrorhandler); if (waimea->wmerr) { cerr << "waimea: warning: another window manager is running on " << displaystring + 8 << endl; waimea->wmerr = true; return; } v_x = v_y = 0; #ifdef RANDR if (waimea->randr) XRRSelectInput(display, id, RRScreenChangeNotifyMask); #endif // RANDR rh->loadConfig(this); #ifdef RENDER if (wa->argb_visual) { Visual *argb_visual = findARGBVisual(); if (argb_visual) { visual = argb_visual; screen_depth = 32; colormap = XCreateColormap (display, id, argb_visual, AllocNone); } else { showWarningMessage (__FUNCTION__, "couldn't find ARGB visual on %s", displaystring); } } #endif // RENDER XGCValues gcv; gcv.foreground = WhitePixel(display, screen_number); white_gc = XCreateGC(display, id, GCForeground, &gcv); gcv.foreground = BlackPixel(display, screen_number); black_gc = XCreateGC(display, id, GCForeground, &gcv); gcv.foreground = WhitePixel(display, screen_number) ^ BlackPixel(display, screen_number); gcv.function = GXxor; gcv.subwindow_mode = IncludeInferiors; xor_gc = XCreateGC(display, id, GCForeground | GCFunction | GCSubwindowMode, &gcv); current_desktop = new Desktop(0, width, height); desktop_list.push_back(current_desktop); for (i = 1; i < config.desktops; i++) desktop_list.push_back(new Desktop(i, width, height)); net->setWorkarea(this); waimea->window_table.insert(make_pair(id, this)); attrib_set.override_redirect = true; wm_check = XCreateWindow(display, id, 0, 0, 1, 1, 0, CopyFromParent, InputOnly, CopyFromParent, CWOverrideRedirect, &attrib_set); windowlist_menu = NULL; v_xmax = (config.virtual_x - 1) * width; v_ymax = (config.virtual_y - 1) * height; readActionLists(); readStyles(); west = new ScreenEdge(this, "west", 0, 0, 2, height); east = new ScreenEdge(this, "east", width - 2, 0, 2, height); north = new ScreenEdge(this, "north", 0, 0, width, 2); south = new ScreenEdge(this, "south", 0, height - 2, width, 2); resetActionList(new WaStringMap(WindowIDName, "root")); net->setDesktopGeometry(this); net->setNumberOfDesktops(this); net->getCurrentDesktop(this); net->setCurrentDesktop(this); net->getDesktopViewPort(this); net->setDesktopViewPort(this); net->getXRootPMapId(this); resetStyle(); pushRenderEvent(); readMenus(); XWindowAttributes attr; XQueryTree(display, id, &ro, &pa, &children, &nchild); for (i = 0; i < nchild; ++i) { XWMHints *wm_hints = NULL; int status = 0; if (children[i] == None) continue; wa_grab_server(); if (validate_drawable(children[i])) { status = XGetWindowAttributes(display, children[i], &attr); wm_hints = XGetWMHints(display, children[i]); } else { wa_ungrab_server(); continue; } wa_ungrab_server(); if (wm_hints) { if ((wm_hints->flags & IconWindowHint) && (wm_hints->icon_window != children[i])) { for (unsigned int j = 0; j < nchild; j++) { if (children[j] == wm_hints->icon_window) { children[j] = None; break; } } } XFree(wm_hints); } } for (i = 0; i < nchild; ++i) { XWMHints *wm_hints = NULL; int state = -1; int status = 0; if (children[i] == None) continue; wa_grab_server(); if (validate_drawable(children[i])) { status = XGetWindowAttributes(display, children[i], &attr); wm_hints = XGetWMHints(display, children[i]); } else { wa_ungrab_server(); continue; } wa_ungrab_server(); if (wm_hints) { if (wm_hints->flags & StateHint) state = wm_hints->initial_state; XFree(wm_hints); } if (status && attr.screen && (! attr.override_redirect) && (attr.map_state != IsUnmapped)) { if (net->isSystrayWindow(children[i])) { if (! (waimea->findWin(children[i], SystrayType))) { XSelectInput(display, children[i], StructureNotifyMask); SystrayWindow *stw = new SystrayWindow(children[i], this); waimea->window_table.insert(make_pair(children[i], stw)); systray_window_list.push_back(children[i]); net->setSystrayWindows(this); } } else { if (state == WithdrawnState) { EventDetail ed; ed.x = ed.y = ed.detail = ed.x11mod = ed.wamod = 0; ed.type = DockappAddRequest; Dockapp *d = new Dockapp(this, children[i]); waimea->eh->evAct(NULL, d->id, &ed); if (! d->dh) addDockapp(d, NULL); } else if ((waimea->window_table.find(children[i])) == waimea->window_table.end()) { WaWindow *newwin = new WaWindow(children[i], this); if ((newwin = (WaWindow *) waimea->findWin(children[i], WindowType))) { net->setState(newwin, newwin->state); net->getMergedState(newwin); list::iterator it = mreqs.begin(); while (it != mreqs.end()) if ((*it)->mid == children[i]) { newwin->merge((*it)->win, (*it)->type); it = mreqs.erase(it); } else it++; } } } } } XFree(children); LISTDEL(mreqs); net->getClientListStacking(this); ws->restackWindows(); net->setClientList(this); net->getActiveWindow(this); net->setSupportedWMCheck(this, wm_check); net->setSupported(this); } WaScreen::~WaScreen(void) { WaFrameWindow *wf; shutdown = true; XSelectInput(display, id, NoEventMask); net->deleteSupported(this); XDestroyWindow(display, wm_check); LISTDELITEMS(docks); WaWindow **delstack = new WaWindow*[wawindow_list.size()]; int stackp = 0; list::reverse_iterator it = aab_stacking_list.rbegin(); for (; it != aab_stacking_list.rend(); ++it) { wf = (WaFrameWindow *) waimea->findWin(*it, WindowFrameType); if (wf) delstack[stackp++] = wf->wa; } it = stacking_list.rbegin(); for (; it != stacking_list.rend(); ++it) { wf = (WaFrameWindow *) waimea->findWin(*it, WindowFrameType); if (wf) delstack[stackp++] = wf->wa; } it = aot_stacking_list.rbegin(); for (; it != aot_stacking_list.rend(); ++it) { wf = (WaFrameWindow *) waimea->findWin(*it, WindowFrameType); if (wf) delstack[stackp++] = wf->wa; } for (int i = 0; i < stackp; i++) delete delstack[i]; delete [] delstack; LISTCLEAR(wawindow_list); LISTCLEAR(wawindow_list_map_order); LISTDEL(strut_list); delete west; delete east; delete north; delete south; net->setXRootPMapId(this, NULL); vector::iterator vit = subwindow_names.begin(); for (; vit != subwindow_names.end(); vit++) delete [] *vit; waimea->window_table.erase(id); } #ifdef RENDER Visual *WaScreen::findARGBVisual(void) { XVisualInfo *xvi; XVisualInfo templ; int nvi; int i; XRenderPictFormat *format; Visual *visual; templ.screen = screen_number; templ.depth = 32; xvi = XGetVisualInfo (display, VisualScreenMask | VisualDepthMask, &templ, &nvi); if (!xvi) return 0; visual = 0; for (i = 0; i < nvi; i++) { format = XRenderFindVisualFormat (display, xvi[i].visual); if (format->type == PictTypeDirect && format->direct.alphaMask) { visual = xvi[i].visual; break; } } XFree (xvi); return visual; } #endif // RENDER void WaScreen::propagateActionlistUpdate(ActionList *actionlist) { if (dont_propagate_cfg_update) return; map::iterator it = waimea->window_table.begin(); for (; it != waimea->window_table.end(); it++) { if (((*it).second)->ws == this && ((*it).second)->type == WindowType) { WaWindow *wa = (WaWindow *) (*it).second; ActionList *al = wa->actionlists[ STATE_FROM_MASK_AND_LIST(window_state_mask, wa->actionlists)]; if (al == actionlist) wa->updateGrabs(); } } } void WaScreen::propagateStyleUpdate(Style *style) { if (dont_propagate_cfg_update) return; map::iterator it = waimea->window_table.begin(); for (; it != waimea->window_table.end(); it++) { if (((*it).second)->ws == this && ((*it).second)->type & ANY_DECOR_WINDOW_TYPE && ((DWindowObject *) (*it).second)->style == style) ((DWindowObject *) (*it).second)->styleUpdate(true, true); } } void WaScreen::forceRenderOfWindows(int typemask) { map::iterator it = waimea->window_table.begin(); for (; it != waimea->window_table.end(); it++) { if (((*it).second)->ws == this && ((*it).second)->type & ANY_DECOR_WINDOW_TYPE && ((*it).second)->type & typemask) { ((DWindowObject *) (*it).second)->force_texture = true; ((DWindowObject *) (*it).second)->pushRenderEvent(); } } } void WaScreen::clearAllCacheAndRedraw(void) { Tst::iterator it = rendergroups.begin(); for (; it != rendergroups.end(); it++) { (*it)->clearCache(); if ((*it)->is_a_style) { ((Style *) *it)->serial++; propagateStyleUpdate((Style *) *it); } } } void WaScreen::reload(void) { RENDER_GET; ws->waimea->prefocus = None; XSetInputFocus(display, None, RevertToNone, CurrentTime); Tst::iterator cit = constants.begin(); for (; cit != constants.end(); cit++) delete [] *cit; constants.clean(); dont_propagate_cfg_update = true; readActionLists(); readStyles(); readMenus(); dont_propagate_cfg_update = false; map::iterator it = waimea->window_table.begin(); for (; it != waimea->window_table.end(); it++) { if (((*it).second)->ws == this && ((*it).second)->type & ANY_ACTION_WINDOW_TYPE) ((AWindowObject *) (*it).second)->resetActionList(); } resetStyle(); commonStyleUpdate(); pushRenderEvent(); list::iterator dock_it = docks.begin(); for (; dock_it != docks.end(); dock_it++) { (*dock_it)->resetStyle(); (*dock_it)->commonStyleUpdate(); (*dock_it)->update(); } list::iterator win_it = wawindow_list.begin(); for (; win_it != wawindow_list.end(); win_it++) { (*win_it)->frame->resetStyle(); (*win_it)->frame->commonStyleUpdate(); (*win_it)->windowStateCheck(true); (*win_it)->updateAllAttributes(); } waimea->focusRevertFrom(this, None); RENDER_RELEASE; } void WaScreen::raiseTransientWindows(Window win, list *stacklist) { WaFrameWindow *wf = (WaFrameWindow *) waimea->findWin(win, WindowFrameType); if (wf) { WaWindow *ww = wf->wa; if (! ww->transients.empty()) { list::iterator tit = ww->transients.begin(); for (; tit != ww->transients.end();) { WaWindow *wt = (WaWindow *) waimea->findWin(*tit, WindowType); if (wt) { stacking_list.remove(wt->frame->id); aab_stacking_list.remove(wt->frame->id); aot_stacking_list.remove(wt->frame->id); stacklist->push_front(wt->frame->id); tit++; } else tit = ww->transients.erase(tit); } } } } void WaScreen::raiseWindow(Window win, bool restack) { list::iterator it = aot_stacking_list.begin(); for (; it != aot_stacking_list.end(); ++it) { if (*it == win) { aot_stacking_list.erase(it); aot_stacking_list.push_front(win); raiseTransientWindows(*it, &aot_stacking_list); if (restack) restackWindows(); return; } } it = stacking_list.begin(); for (; it != stacking_list.end(); ++it) { if (*it == win) { stacking_list.erase(it); stacking_list.push_front(win); raiseTransientWindows(*it, &stacking_list); if (restack) restackWindows(); return; } } it = aab_stacking_list.begin(); for (; it != aab_stacking_list.end(); ++it) { if (*it == win) { aab_stacking_list.erase(it); aab_stacking_list.push_front(win); raiseTransientWindows(*it, &aab_stacking_list); if (restack) restackWindows(); return; } } } void WaScreen::lowerWindow(Window win, bool restack) { list::iterator it = aot_stacking_list.begin(); for (; it != aot_stacking_list.end(); ++it) { if (*it == win) { aot_stacking_list.erase(it); aot_stacking_list.push_back(win); if (restack) restackWindows(); return; } } it = stacking_list.begin(); for (; it != stacking_list.end(); ++it) { if (*it == win) { stacking_list.erase(it); stacking_list.push_back(win); if (restack) restackWindows(); return; } } it = aab_stacking_list.begin(); for (; it != aab_stacking_list.end(); ++it) { if (*it == win) { aab_stacking_list.erase(it); aab_stacking_list.push_back(win); if (restack) restackWindows(); return; } } } void WaScreen::restackWindows(void) { int i = 0; Window *stack = new Window[4 + aot_stacking_list.size() + stacking_list.size() + aab_stacking_list.size()]; stack[i++] = west->id; stack[i++] = east->id; stack[i++] = north->id; stack[i++] = south->id; list::iterator it = aot_stacking_list.begin(); for (; it != aot_stacking_list.end(); ++it) stack[i++] = *it; it = stacking_list.begin(); for (; it != stacking_list.end(); ++it) stack[i++] = *it; it = aab_stacking_list.begin(); for (; it != aab_stacking_list.end(); ++it) stack[i++] = *it; XRestackWindows(display, stack, i); delete [] stack; } ActionList *WaScreen::getActionListNamed(char *name, bool warn) { if (! name) return NULL; Tst::iterator it = actionlists.find(name); if (it != actionlists.end()) return *it; if (warn) showWarningMessage(__FUNCTION__, "unknown actionlist=%s", name); return NULL; } Style *WaScreen::getStyleNamed(char *name, bool warn) { if (! name) return NULL; Tst