/* * Copyright (C) 1999,2000,2001 Frank Hale * frankhale@yahoo.com * http://sapphire.sourceforge.net/ * * Updated: 3 Nov 2001 * * 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 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "windowmanager.hh" WindowManager *wm; char* windowmanager_author = "Frank Hale"; char* windowmanager_author_email = "frankhale@yahoo.com"; char* windowmanager_name = "sapphire"; char* windowmanager_version = "Sapphire 0.15.7pre7 (Blackberry)"; char* windowmanager_usage = "[-display ] [-version]\n"; char* windowmanager_exit_message = "\nThank you for using Sapphire.\n\nPlease Send all bug reports, suggestions, ideas, patches to frankhale@yahoo.com\n"; char* configuration_home = "/usr/X11R6/share/sapphire"; char* HOME; // This function sets up default values for variables, gets command line args, // sets up our signal handlers, and performs initialization of certain things // to get ready for the window manager to start managing windows. WindowManager::WindowManager(int argc, char **argv) { ::wm = this; HOME = new char[strlen(getenv("HOME"))+1]; strcpy(HOME, getenv("HOME")); font_pix = None; menu_event = false; clock_format = "%m %d %H:%M"; toolbar_visible = true; toolbar_position = false; configurationPath = NULL; windowmanager_conf = NULL; display = NULL; XSetWindowAttributes attr; XColor dummy; head_client = new Client(); // Allocate memory for our linked lists. ClientList = new LinkedList; menulist = new LinkedList; extramenulist = new LinkedList; for (int i = 1; i < argc; i++) { if ((strcmp(argv[i], "-display")==0) && i+1dpy, XC_left_ptr)); // Atoms xa_wm_state = XInternAtom(dpy, "WM_STATE", False); xa_wm_change_state = XInternAtom(dpy, "WM_CHANGE_STATE", False); xa_wm_protos = XInternAtom(dpy, "WM_PROTOCOLS", False); xa_wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False); xa_wm_cmapwins = XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False); xa_wm_take_focus = XInternAtom(dpy, "WM_TAKE_FOCUS", False); xa_wm_motif_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False); foreground_color = DEFAULT_FOREGROUND_COLOR; background_color = DEFAULT_BACKGROUND_COLOR; border_color = DEFAULT_BORDER_COLOR; // Colors XAllocNamedColor(dpy, DefaultColormap(dpy, screen), foreground_color, &xforeground_color, &dummy); XAllocNamedColor(dpy, DefaultColormap(dpy, screen), background_color, &xbackground_color, &dummy); XAllocNamedColor(dpy, DefaultColormap(dpy, screen), border_color, &xborder_color, &dummy); colors_per_channel = 4; image_dither = True; image_control = new BImageControl(); image_control->installRootColormap(); theme = new Theme(); getCurrentTheme(); font = theme->getFont(); menufont = theme->getMenuFont(); xfont = XLoadQueryFont(dpy, font); if (!xfont) { // We should really check here to see if we can // find another font just incase. Dying here without // checking again is not cool! err("the default font was not found, aborting."); exit(-1); } iheight = xfont->ascent + xfont->descent + 2 * DEFAULT_SPACE; font_position = iheight - xfont->descent; if(! QueryShapeExtentions()) { printf("What no shape extentions, God you must be using an old X11 release.\n\nHave a nice life\n\nBa Bye...\n\n"); } move_curs = XCreateFontCursor(dpy, XC_fleur); resize_curs = XCreateFontCursor(dpy, XC_plus); left_arrow = XCreateFontCursor(dpy, XC_left_ptr); createFontPixmap(); gv.font = xfont->fid; gv.tile = font_pix; string_gc = XCreateGC(dpy, root, GCTile | GCFont , &gv); XSetFillStyle(dpy, string_gc, FillTiled); gv.foreground = xborder_color.pixel; gv.line_width = DEFAULT_BORDER_SIZE; border_gc = XCreateGC(dpy, root, GCForeground | GCLineWidth, &gv); gv.function = GXinvert; gv.subwindow_mode = IncludeInferiors; invert_gc = XCreateGC(dpy, root, GCFunction | GCSubwindowMode | GCLineWidth, &gv); attr.event_mask = SubstructureRedirectMask | SubstructureNotifyMask | ColormapChangeMask | ButtonPressMask | ButtonReleaseMask | PropertyChangeMask | PointerMotionMask | FocusChangeMask | KeyPressMask | ExposureMask ; XChangeWindowAttributes(dpy, root, CWEventMask, &attr); xres = WidthOfScreen(ScreenOfDisplay(wm->dpy, wm->screen)); yres = HeightOfScreen(ScreenOfDisplay(wm->dpy, wm->screen)); rm = new RootMenu(); t = new Toolbar(); im = new IconMenu(); addToMenuList(rm); rm->parseMenuConfigurationFile(); t->getToolbarMenu()->addMenu("icon menu", im); t->getToolbarMenu()->addMenu("root menu", rm); t->getToolbarMenu()->update_all(); scan_windows(); do_event_loop(); } WindowManager::~WindowManager() { unsigned int i, nwins; Window dw1, dw2, *wins; Client *temp_client=NULL; XQueryTree(dpy, root, &dw1, &dw2, &wins, &nwins); for (i = 0; i < nwins; i++) { temp_client = head_client->find_client(wins[i]); if(temp_client) { temp_client->remove_client(temp_client); } } XFree(wins); ClientList->removeAll(); delete head_client; delete ClientList; image_control->removeImage(font_pix); XFreeFont(dpy, xfont); XFreeGC(dpy, invert_gc); XFreeGC(dpy, string_gc); XFreeGC(dpy, border_gc); delete t; delete rm; delete im; menulist->removeAll(); extramenulist->removeAll(); delete menulist; delete extramenulist; delete theme; delete image_control; delete [] HOME; delete [] windowmanager_conf; delete [] configurationPath; } void WindowManager::getCurrentTheme() { XrmDatabase resource_db; XrmValue value; char *value_type; FILE* currentTheme; bool themeLoaded = false; windowmanager_conf = new char[strlen(HOME) + 32]; sprintf(windowmanager_conf, "%s/.sapphire/wmconf", HOME); if((currentTheme = fopen(windowmanager_conf, "r"))!=NULL) { fclose(currentTheme); XrmInitialize(); resource_db = XrmGetFileDatabase(windowmanager_conf); if (XrmGetResource(resource_db, "toolbar.visible", "Toolbar.Visible", &value_type, &value)) { if(strcmp(value.addr, "true")==0) { theme->setToolbarVisible(); toolbar_visible=true; } else { toolbar_visible=false; } } if (XrmGetResource(resource_db, "toolbar.position", "Toolbar.Position", &value_type, &value)) { if(strcmp(value.addr, "top")==0) { theme->setToolbarTop(); toolbar_position = true; } else { toolbar_position = false; } } // Clock format if (XrmGetResource(resource_db,"clock.format","Clock.Format",&value_type,&value)) { clock_format= new char[value.size]; strcpy(clock_format,value.addr); } else { clock_format="%m %d %H:%M"; } if (XrmGetResource(resource_db, "start.theme", "Start.Theme", &value_type, &value)) { char *buffer = new char[value.size+strlen(HOME)]; strcpy(buffer, value.addr); if (value.addr[0] == '~') { sprintf(buffer, "%s%s", HOME, value.addr+1); } if((currentTheme = fopen(buffer, "r"))!=NULL) { fclose(currentTheme); theme->parseTheme(buffer); themeLoaded=true; } delete [] buffer; } XrmDestroyDatabase(resource_db); } if (! themeLoaded) { char* defaultTheme = new char[strlen(configuration_home) + 32]; sprintf(defaultTheme, "%s/themes/default.theme", configuration_home); theme->parseTheme(defaultTheme); theme->setToolbarVisible(); delete [] defaultTheme; } } void WindowManager::createFontPixmap() { unsigned long style=0; style = wm->getTheme()->getFontStyle(); font_pix = wm->getImageControl()->renderImage( //"xfont", xfont->ascent, xfont->descent, style, wm->getTheme()->getFontColorFrom(), wm->getTheme()->getFontColorTo() ); } // We really should have some way to check if the menu needs // reconfiguring. It shouldn't do anymore processing than // it needs to. void WindowManager::reconfigureMenu() { XGrabServer(dpy); if(menulist->count()) { menulist->removeAll(); rm->removeAll(); addToMenuList(rm); rm->parseMenuConfigurationFile(); } XUngrabServer(dpy); } int WindowManager::QueryShapeExtentions() { int dummy2; return shape = XShapeQueryExtension(dpy, &shape_event, &dummy2); } void WindowManager::scan_windows() { unsigned int i, nwins; Window dw1, dw2, *wins; XWindowAttributes attr; XQueryTree(dpy, root, &dw1, &dw2, &wins, &nwins); for (i = 0; i < nwins; i++) { XGetWindowAttributes(dpy, wins[i], &attr); if (!attr.override_redirect && attr.map_state == IsViewable) head_client->make_new_client(wins[i]); } XFree(wins); } void WindowManager::do_event_loop() { XEvent ev; BaseMenu *m = None; int xfd = ConnectionNumber(wm->dpy); int dx=0,dy=0; for (;;) { if(XPending(wm->dpy)) { XNextEvent(wm->dpy, &ev); switch (ev.type) { case ButtonPress: { head_client->handle_button_press_event(&ev.xbutton); t->do_button_press_event(&ev.xbutton); m = findInMenuList(&ev.xbutton.window); if(m) { m->handle_button_press_event(&ev.xbutton); } switch(ev.xbutton.button) { case 2: { if(ev.xbutton.window == root) { t->setStartMenuButtonStateNotPressed(); im->update_all(); im->setAllMenuTitlesVisible(im,True); im->show(); } break; } case 3: { if(ev.xbutton.window == root) { t->setStartMenuButtonStateNotPressed(); rm->hideAllVisibleSubmenus(); reconfigureMenu(); rm->setAllMenuTitlesVisible(rm, True); rm->show(); } break; } default: { if(ev.xbutton.window == root) { t->setStartMenuButtonStateNotPressed(); im->setAllMenuTitlesVisible(im, False); im->hide(im); rm->setAllMenuTitlesVisible(rm, False); rm->hideAllVisibleSubmenus(); rm->hide(rm); } } break; } break; } case ButtonRelease: { m = findInMenuList(&ev.xbutton.window); if(m) m->handle_button_release_event(&ev.xbutton); switch(ev.xbutton.button) { case 1: case 2: case 3: default: { if(menu_event) { wm->ungrabPointer(); menu_event=false; } } break; } break; } case ConfigureRequest: { head_client->handle_configure_request(&ev.xconfigurerequest); break; } case ConfigureNotify: { break; } case DestroyNotify: { head_client->handle_destroy_notify(&ev.xdestroywindow); break; } case MapRequest: { head_client->handle_map_request(&ev.xmaprequest); break; } case UnmapNotify: { head_client->handle_unmap_event(&ev.xunmap); break; } case ClientMessage: { head_client->handle_client_message(&ev.xclient); break; } case ColormapNotify: { head_client->handle_colormap_change(&ev.xcolormap); break; } case PropertyNotify: { head_client->handle_property_change(&ev.xproperty); break; } case LeaveNotify: { m = findInMenuList(&ev.xcrossing.window); if(m) m->handle_leave_notify(&ev.xcrossing); break; } case EnterNotify: { head_client->handle_enter_event(&ev.xcrossing); m = findInMenuList(&ev.xcrossing.window); if(m) m->handle_enter_notify(&ev.xcrossing); break; } case MotionNotify: { if(menu_event) { grabPointer(root, ButtonReleaseMask | PointerMotionMask, move_curs); dx = ev.xmotion.x_root - menu->menu.x_move; dy = ev.xmotion.y_root - menu->menu.y_move; menu->menu.x = dx; menu->menu.y = dy; XMoveWindow(dpy, menu->menu.window, dx, dy); } m = findInMenuList(&ev.xmotion.window); if(m) m->handle_motion_notify_event(&ev.xmotion); break; } case Expose: { head_client->handle_expose_event(&ev.xexpose); t->handle_expose_event(&ev.xexpose); rm->handle_expose_event(&ev.xexpose); m = findInMenuList(&ev.xexpose.window); if(m) m->handle_expose_event(&ev.xexpose); break; } default: { if (wm->shape && ev.type == wm->shape_event) head_client->handle_shape_change((XShapeEvent *)&ev); break; } } } else { fd_set rfds; FD_ZERO(&rfds); FD_SET(xfd, &rfds); struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 1000; select(xfd + 1, &rfds, 0, 0, &tv); t->checkClock(); } } } // Function: // // void WindowManager::quit_nicely() // // Purpose: // // This functions sole purpose in life is to preform clean up so that we can // shutdown Sapphire safely. // // Returns: // // This function returns nothing. // void WindowManager::quit_nicely() { saveCurrentTheme(); this->~WindowManager(); XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); XInstallColormap(dpy, DefaultColormap(dpy, screen)); XCloseDisplay(dpy); printf("%s", windowmanager_exit_message); exit(0); } void WindowManager::restart() { saveCurrentTheme(); this->~WindowManager(); XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); XInstallColormap(dpy, DefaultColormap(dpy, screen)); XCloseDisplay(dpy); execl("/bin/sh", "sh", "-c", windowmanager_name, 0); } BaseMenu* WindowManager::findInMenuList(Window *w) { if(extramenulist->count()) { LinkedListIterator mx(extramenulist); for(; mx.current(); mx++) { if(*w == mx.current()->getMenuWindow() || *w == mx.current()->getMenuTitleWindow() || *w == mx.current()->getMenuItemWindow()) { return mx.current(); } } } if(menulist->count()) { LinkedListIterator my(menulist); for(; my.current(); my++) { if(*w == my.current()->getMenuWindow() || *w == my.current()->getMenuTitleWindow() || *w == my.current()->getMenuItemWindow()) { return my.current(); } } } return NULL; } void WindowManager::grabPointer(Window w, unsigned int mask, Cursor curs) { if (! XGrabPointer(dpy, w, False, mask, GrabModeAsync, GrabModeAsync, None, curs, CurrentTime) == GrabSuccess ) return; } void WindowManager::ungrabPointer() { XUngrabPointer(dpy, CurrentTime); } void WindowManager::changeTheme(char* themeFile) { image_control->removeAllImages(); theme->parseTheme(themeFile); XFreeFont(dpy, xfont); XFreeGC(dpy, string_gc); if(font_pix) XFreePixmap(dpy, font_pix); xfont = XLoadQueryFont(dpy, theme->getFont()); if(!xfont) { err("the font specified in the current theme was not found, using default font."); xfont = XLoadQueryFont(dpy, font); } font_position = iheight - xfont->descent; createFontPixmap(); gv.font = xfont->fid; gv.tile = font_pix; string_gc = XCreateGC(dpy, root, GCTile | GCFont , &gv); XSetFillStyle(dpy, string_gc, FillTiled); t->changeTheme(); if(ClientList->count()) { LinkedListIterator c(ClientList); for(; c.current(); c++) { c.current()->themeChange(c.current()); } } if(menulist->count()) { LinkedListIterator mx(menulist); for(; mx.current(); mx++) { mx.current()->reconfigure(); } } if(extramenulist->count()) { LinkedListIterator my(extramenulist); for(; my.current(); my++) { my.current()->reconfigure(); } } } void WindowManager::saveCurrentTheme() { // Hopefully I've gotten the bugs out of this function // Only time will tell.... char* save_theme = theme->getCurrentTheme(); FILE *wmconf; if((wmconf = fopen(windowmanager_conf, "w"))==NULL) { char *sapphire_config_dir = new char[strlen(HOME)+30]; sprintf(sapphire_config_dir, "%s/.sapphire", HOME); printf("sapphire_config_dir = %s\n", sapphire_config_dir); mkdir(sapphire_config_dir, S_IRWXU); delete [] sapphire_config_dir; if((wmconf = fopen(windowmanager_conf, "w"))==NULL) { perror("error:\n"); exit(-1); } } if(toolbar_visible) fprintf(wmconf, "toolbar.visible: true\n"); else fprintf(wmconf, "toolbar.visible: false\n"); if(toolbar_position) fprintf(wmconf, "toolbar.position: top\n"); else fprintf(wmconf, "toolbar.position: bottom\n"); fprintf(wmconf, "start.theme: %s\n", save_theme); fprintf(wmconf, "clock.format: %s\n", clock_format); //fprintf(wmconf, "desktops.count: %d\n", max_desktops); fclose(wmconf); }