/* * $Id: Windowmanager.cpp 1711 2006-07-25 09:51:50Z karijes $ * * Edewm, window manager * Part of Equinox Desktop Environment (EDE). * Copyright (c) 2000-2006 EDE Authors. * * This program is licenced under terms of the * GNU General Public Licence version 2 or newer. * See COPYING for details. */ #include "Windowmanager.h" #include "Atoms.h" #include "Hints.h" #include "Frame.h" #include "../exset/exset.h" #include "Tracers.h" #include "Sound.h" #include "debug.h" #include #include #include #include #define WM_CONFIG_FILE "wmanager.conf" #define EDE_CONFIG_FILE "ede.conf" WindowManager* WindowManager::pinstance = NULL; int x_errors; /* This is one of the most important part of wm and reflects * current design. Wm will try to send all messages to frame itself, * after trying to find it's ID in collected list. All further processing * is left to that frame. Other messages will process wm, * minimizing spreading events all over the code. * I'am not in love with this decision; it's roots are from previous * edewm code (the real roots are from icewm). * * Future major versions will probably have different design. */ int wm_event_handler(int e) { if(fl_xevent.type == KeyPress) e = FL_KEY; if(!e) { Window window = fl_xevent.xany.window; switch(fl_xevent.type) { case CirculateNotify: case CirculateRequest: case ConfigureNotify: case ConfigureRequest: case CreateNotify: case GravityNotify: /*case MapNotify:*/ case MapRequest: case ReparentNotify: case UnmapNotify: window = fl_xevent.xmaprequest.window; break; } FrameList::iterator last = WindowManager::instance()->window_list.end(); for(FrameList::iterator it = WindowManager::instance()->window_list.begin(); it != last; ++it) { Frame* c = *it; if(c->window() == window || fl_xid(c) == window) { //ELOG("wm_event_handler-> window found (%i), sending a message", i); return c->handle(&fl_xevent); } } return WindowManager::instance()->handle(&fl_xevent); } else return WindowManager::instance()->handle(e); } int convert_align(int a) { switch(a) { default: case 0: break; case 1: return FL_ALIGN_RIGHT; case 2: return FL_ALIGN_CENTER; } return FL_ALIGN_LEFT; } int xerror_handler(Display* d, XErrorEvent* e) { if(e->request_code == X_ChangeWindowAttributes && e->error_code == BadAccess && e->resourceid == RootWindow(fl_display, DefaultScreen(fl_display))) { // force cleaning data WindowManager::shutdown(); Fl::fatal(_("Another window manager is running. You must exit it before running edewm.")); } x_errors++; char buff[128]; EPRINTF("\n"); XGetErrorDatabaseText(fl_display, "XlibMessage", "XError", "", buff, 128); EPRINTF("%s: ", buff); XGetErrorText(fl_display, e->error_code, buff, 128); EPRINTF("%s \n", buff); XGetErrorDatabaseText(fl_display, "XlibMessage", "MajorCode", "%d", buff, 128); EPRINTF(" "); EPRINTF(buff, e->request_code); sprintf(buff, "%d", e->request_code); XGetErrorDatabaseText(fl_display, "XRequest", buff, "%d", buff, 128); EPRINTF(" (%s)\n", buff); XGetErrorDatabaseText(fl_display, "XlibMessage", "MinorCode", "%d", buff, 128); EPRINTF(" "); EPRINTF(buff, e->minor_code); EPRINTF(" "); XGetErrorDatabaseText(fl_display, "XlibMessage", "ResourceID", "%d", buff, 128); EPRINTF(buff, e->resourceid); EPRINTF("\n"); EPRINTF("\n"); return 0; } bool ValidateDrawable(Drawable d) { Window w; int dummy; unsigned int dummy_ui; XSync(fl_display, False); x_errors = 0; XGetGeometry(fl_display, d, &w, &dummy, &dummy, &dummy_ui, &dummy_ui, &dummy_ui, &dummy_ui); XSync(fl_display, False); bool ret = (x_errors == 0 ? true : false); x_errors = 0; /* if(ret != true) { WindowManager::shutdown(); assert(ret == true); } */ return ret; } WindowManager::WindowManager() : Fl_Window(0, 0, Fl::w(), Fl::h()), is_running(false) { box(FL_NO_BOX); ELOG("WindowManager constructor"); } WindowManager::~WindowManager() { ELOG("WindowManager destructor"); FrameList::iterator last = window_list.end(); for(FrameList::iterator it = window_list.begin(); it != last; ++it) { Frame* f = *it; delete f; } window_list.clear(); sound_system->shutdown(); delete sound_system; delete wm_conf; delete hint_stuff; delete cur; } WindowManager* WindowManager::instance(void) { assert(WindowManager::pinstance != NULL); return WindowManager::pinstance; } void WindowManager::init(int argc, char** argv) { if(WindowManager::pinstance != NULL) return; WindowManager::pinstance = new WindowManager(); WindowManager::pinstance->init_internals(); } void WindowManager::shutdown(void) { if(WindowManager::pinstance != NULL) { delete WindowManager::pinstance; WindowManager::pinstance = NULL; } } void WindowManager::init_internals(void) { ELOG("Starting window manager"); wm_conf = new WindowManagerConfig; app_starting = false; // defaults, in case world goes down wm_conf->title_active_color = fl_rgb(0,0,128); wm_conf->title_active_color_text = fl_rgb(255,255,255); wm_conf->title_normal_color = fl_rgb(192,192,192); wm_conf->title_normal_color_text = fl_rgb(0,0,128); wm_conf->title_label_align = FL_ALIGN_LEFT; wm_conf->title_height = 20; wm_conf->title_box_type = 0; wm_conf->frame_do_opaque = false; wm_conf->frame_animate = true; wm_conf->frame_animate_speed = 15; fl_open_display(); XSetErrorHandler(xerror_handler); wm_area.set(0, 0, Fl::w(), Fl::h()); read_configuration(); read_xset_configuration(); //register_protocols(); #ifdef _DEBUG InitAtoms(fl_display, atom_map); register_events(); #else InitAtoms(fl_display); #endif //cur = XCreateFontCursor(fl_display, XC_left_ptr); //XDefineCursor(fl_display, RootWindow(fl_display, fl_screen), cur); // load cursor cur = new CursorHandler; cur->load(X_CURSORS); cur->set_root_cursor(); sound_system = new SoundSystem(); sound_system->init(); sound_system->add(SOUND_MINIMIZE, "sounds/minimize.ogg"); sound_system->add(SOUND_MAXIMIZE, "sounds/maximize.ogg"); sound_system->add(SOUND_CLOSE, "sounds/close.ogg"); sound_system->add(SOUND_RESTORE, "sounds/restore.ogg"); sound_system->add(SOUND_SHADE, "sounds/shade.ogg"); // the world is starting here show(); register_protocols(); hint_stuff = new Hints; hint_stuff->icccm_set_iconsizes(this); init_clients(); Fl::add_handler(wm_event_handler); XSync(fl_display, 0); is_running = true; } // load current visible clients void WindowManager::init_clients(void) { Frame* frame = 0; uint win_num; Window w1, w2, *wins; XWindowAttributes attr; XQueryTree(fl_display, fl_xid(this), &w1, &w2, &wins, &win_num); // XXX: excluding root parent !!! //for (uint i = 0; i < win_num-1; i++) for (uint i = 0; i < win_num; i++) { XGetWindowAttributes(fl_display, wins[i], &attr); if(!attr.override_redirect && attr.map_state == IsViewable) { if(!attr.screen) { ELOG("Screen not as window, skiping..."); continue; } if(ValidateDrawable(wins[i])) frame = new Frame(wins[i], &attr); } else ELOG("Skipping override_redirect window"); } XFree((void *)wins); } // register type messages wm understainds void WindowManager::register_protocols(void) { ELOG("Loading protocols"); SetSupported(root_win); } void WindowManager::read_configuration(void) { ELOG("Reading config"); Fl_Config conf(fl_find_config_file(WM_CONFIG_FILE, 0)); conf.set_section("TitleBar"); conf.read("Active color", wm_conf->title_active_color, fl_rgb(0, 0, 128)); conf.read("Normal color", wm_conf->title_normal_color, fl_rgb(192, 192, 192)); conf.read("Active color text", wm_conf->title_active_color_text, fl_rgb(255, 255, 255)); conf.read("Normal color text", wm_conf->title_normal_color_text, fl_rgb(0, 0, 128)); conf.read("Box type", wm_conf->title_box_type, 0); conf.read("Height", wm_conf->title_height, 20); int la; conf.read("Text align", la, 0); wm_conf->title_label_align = convert_align(la); conf.set_section("Resize"); conf.read("Opaque resize", wm_conf->frame_do_opaque, false); conf.read("Animate", wm_conf->frame_animate, true); conf.read("Animate Speed", wm_conf->frame_animate_speed, 15); conf.set_section("Misc"); conf.read("Use theme", wm_conf->use_theme); notify_clients(); } void WindowManager::read_xset_configuration(void) { Fl_Config conf(fl_find_config_file(EDE_CONFIG_FILE, 1)); int val1, val2, val3; Exset xset; conf.set_section("Mouse"); conf.read("Accel", val1, 4); conf.read("Thress",val2, 4); xset.set_mouse(val1, val2); conf.set_section("Bell"); conf.read("Volume", val1, 50); conf.read("Pitch", val2, 440); conf.read("Duration", val3, 200); xset.set_bell(val1, val2, val3); conf.set_section("Keyboard"); conf.read("Repeat", val1, 1); conf.read("ClickVolume", val2, 50); xset.set_keybd(val1, val2); conf.set_section("Screen"); conf.read("Delay", val1, 15); conf.read("Pattern",val2, 2); xset.set_pattern(val1, val2); conf.read("CheckBlank", val1, 1); xset.set_check_blank(val1); conf.read("Pattern", val1, 2); xset.set_blank(val1); } void WindowManager::notify_clients(void) { #warning "TODO: implement WindowManager::notify_clients()" } void WindowManager::show(void) { if(!shown()) { create(); /* Destroy efltk window, set RootWindow to our * xid and redirect all messages to us, which * will make us a window manager. */ XDestroyWindow(fl_display, Fl_X::i(this)->xid); Fl_X::i(this)->xid = RootWindow(fl_display, fl_screen); root_win = RootWindow(fl_display, fl_screen); XSelectInput(fl_display, fl_xid(this), SubstructureRedirectMask | SubstructureNotifyMask | ColormapChangeMask | PropertyChangeMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | KeyPressMask | KeyReleaseMask | KeymapStateMask); ELOG("RootWindow ID set to xid"); draw(); } } void WindowManager::draw(void) { ELOG("RootWindow draw"); // redraw root window XClearWindow(fl_display, fl_xid(this)); } void WindowManager::idle(void) { //ELOG("Idle"); FrameList::iterator last = remove_list.end(); for(FrameList::iterator it = remove_list.begin(); it != last; ++it) { Frame* f = *it; delete f; } remove_list.clear(); } void WindowManager::exit(void) { if(is_running) is_running = false; } const Cursor WindowManager::root_cursor(void) { assert(cur != NULL); return cur->root_cursor(); } void WindowManager::set_cursor(Frame* f, CursorType t) { assert(f != NULL); cur->set_cursor(f, t); /* if(cur->cursor_shape_type() == FLTK_CURSORS) cur->set_fltk_cursor(f, t); else cur->set_x_cursor(f, t); */ } const CursorHandler* WindowManager::cursor_handler(void) { assert(cur != NULL); return cur; } int WindowManager::handle(int event) { Window window = fl_xevent.xany.window; switch(event) { case FL_PUSH: { FrameList::iterator last = window_list.end(); for(FrameList::iterator it = window_list.begin(); it != last; ++it) { Frame* f = *it; if(f->window() == window || fl_xid(f) == window) { f->content_click(); return 1; } } ELOG("FL_PUSH on root"); return 0; } case FL_SHORTCUT: case FL_KEY: ELOG("FL_SHORCUT | FL_KEY"); return 1; case FL_MOUSEWHEEL: XAllowEvents(fl_display, ReplayPointer, CurrentTime); return 0; } return 0; } int WindowManager::handle(XEvent* event) { switch(event->type) { /* ClientMessage is only used for desktop handling * and startup notifications. */ case ClientMessage: { ELOG("ClientMessage in wm"); if(event->xclient.message_type == _XA_EDE_WM_STARTUP_NOTIFY) { Atom data = event->xclient.data.l[0]; if(data == _XA_EDE_WM_APP_STARTING) { app_starting = true; cur->set_root_cursor(CURSOR_WAIT); } } return 1; } case MapRequest: { ELOG("MapRequest from wm"); const XMapRequestEvent* e = &(fl_xevent.xmaprequest); XWindowAttributes attrs; XGetWindowAttributes(fl_display, e->window, &attrs); if(!attrs.override_redirect) { ELOG("--- map from wm ---"); new Frame(e->window); if(app_starting) { cur->set_root_cursor(CURSOR_DEFAULT); app_starting = false; } } return 1; } case ConfigureRequest: { ELOG("ConfigureRequest from wm"); const XConfigureRequestEvent *e = &(fl_xevent.xconfigurerequest); XConfigureWindow(fl_display, e->window, e->value_mask&~(CWSibling|CWStackMode), (XWindowChanges*)&(e->x)); return 1; } default: return 0; } return 0; } /* Clear stack_list and window_list for those * windows schedulied for removal. * Expensive operation, so use it with care. */ void WindowManager::update_client_list(void) { bool found = false; // first clear aot_list FrameList::iterator last = aot_list.end(); for(FrameList::iterator it = aot_list.begin(); it != last; ++it) { Frame* f = *it; if(f->destroy_scheduled()) { // erase current and let 'it' point to next element it = aot_list.erase(it); found = true; } } // then clear stack_list if(!found) { last = stack_list.end(); for(FrameList::iterator it = stack_list.begin(); it != last; ++it) { Frame* f = *it; if(f->destroy_scheduled()) it = stack_list.erase(it); } } // then window_list last = window_list.end(); for(FrameList::iterator it = window_list.begin(); it != last; ++it) { Frame* f = *it; if(f->destroy_scheduled()) { // TODO: do I need this ??? remove_list.push_back(*it); it = window_list.erase(it); } } } /* Used by frames. * Window manager will check position of last client and * return possible next one * * Accepted values are: current frame width and height, and * returned is position where it should be placed. */ bool WindowManager::query_best_position(int* x, int* y, int w, int h) { const int offset = 20; if(window_list.size() <= 0) return false; //Frame* f = window_list[window_list.size()-1]; Frame* f = *(--window_list.end()); if(!f) return false; *x = f->x() + offset; *y = f->y() + offset; // if w-h of frame are larger than area // place them to apropriate corners if((*x + w) > wm_area.w()) *x = wm_area.x(); if((*y + h) > wm_area.h()) *y = wm_area.y(); return true; } Frame* WindowManager::find_xid(Window win) { FrameList::iterator last = window_list.end(); for(FrameList::iterator it = window_list.begin(); it != last; ++it) { Frame* f = *it; if(f->window() == win) return f; } return 0; } void WindowManager::restack_windows(void) { TRACE_FUNCTION("void WindowManager::restack_windows(void)"); Window* stack = new Window[aot_list.size() + stack_list.size()]; FrameList::iterator it = aot_list.begin(); FrameList::iterator last = aot_list.end(); unsigned int i = 0; for(; it != last && i < aot_list.size(); ++it, i++) stack[i] = fl_xid(*it); it = stack_list.begin(); last = stack_list.end(); for(; it != last && i < aot_list.size() + stack_list.size(); ++it, i++) stack[i] = fl_xid(*it); XRestackWindows(fl_display, stack, stack_list.size()); delete [] stack; } void WindowManager::clear_focus_windows(void) { if(aot_list.size() > 0) { FrameList::iterator it = aot_list.begin(); FrameList::iterator last = aot_list.end(); for(; it != last; ++it) (*it)->unfocus(); } FrameList::iterator it = stack_list.begin(); FrameList::iterator last = stack_list.end(); for(; it != last; ++it) (*it)->unfocus(); } void WindowManager::play_sound(short event) { assert(sound_system != NULL); sound_system->play(event); } #ifdef _DEBUG void WindowManager::register_events(void) { xevent_map[CirculateNotify] = "CirculateNotify"; xevent_map[CirculateRequest] = "CirculateRequest"; xevent_map[ConfigureNotify] = "ConfigureNotify"; xevent_map[ConfigureRequest] = "ConfigureRequest"; xevent_map[CreateNotify] = "CreateNotify"; xevent_map[GravityNotify] = "GravityNotify"; xevent_map[MapNotify] = "MapNotify"; xevent_map[MapRequest] = "MapRequest"; xevent_map[ReparentNotify] = "ReparentNotify"; xevent_map[UnmapNotify] = "UnmapNotify"; xevent_map[DestroyNotify] = "DestroyNotify"; xevent_map[PropertyNotify] = "PropertyNotify"; xevent_map[EnterNotify] = "EnterNotify"; xevent_map[LeaveNotify] = "LeaveNotify"; xevent_map[VisibilityNotify] = "VisibilityNotify"; xevent_map[FocusIn] = "FocusIn"; xevent_map[FocusOut] = "FocusOut"; xevent_map[ClientMessage] = "ClientMessage"; efltkevent_map[FL_PUSH] = "FL_PUSH"; efltkevent_map[FL_RELEASE] = "FL_RELEASE"; efltkevent_map[FL_ENTER] = "FL_ENTER"; efltkevent_map[FL_LEAVE] = "FL_LEAVE"; efltkevent_map[FL_DRAG] = "FL_DRAG"; efltkevent_map[FL_FOCUS] = "FL_FOCUS"; efltkevent_map[FL_UNFOCUS] = "FL_UNFOCUS"; efltkevent_map[FL_KEY] = "FL_KEY"; efltkevent_map[FL_KEYUP] = "FL_KEYUP"; efltkevent_map[FL_MOVE] = "FL_MOVE"; efltkevent_map[FL_SHORTCUT] = "FL_SHORTCUT"; efltkevent_map[FL_ACTIVATE] = "FL_ACTIVATE"; efltkevent_map[FL_DEACTIVATE]= "FL_DEACTIVATE"; efltkevent_map[FL_SHOW] = "FL_SHOW"; efltkevent_map[FL_HIDE] = "FL_HIDE"; efltkevent_map[FL_MOUSEWHEEL]= "FL_MOUSEWHEEL"; efltkevent_map[FL_PASTE] = "FL_PASTE"; efltkevent_map[FL_DND_ENTER] = "FL_DND_ENTER"; efltkevent_map[FL_DND_DRAG] = "FL_DND_DRAG"; efltkevent_map[FL_DND_LEAVE] = "FL_DND_LEAVE"; efltkevent_map[FL_DND_RELEASE] = "FL_DND_RELEASE"; } #endif