/* * Matchbox Window Manager - A lightweight window manager not for the * desktop. * * Authored By Matthew Allum * * Copyright (c) 2002, 2004 OpenedHand Ltd - http://o-hand.com * * 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, 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. * */ #include "wm.h" #include "config.h" #ifdef HAVE_XFIXES #include /* Used to *really* hide cursor */ #endif #ifdef HAVE_XCURSOR #include #endif #ifdef USE_XSETTINGS static void wm_xsettings_notify_cb (const char *name, XSettingsAction action, XSettingsSetting *setting, void *data); #endif #ifdef USE_LIBSN static void wm_sn_timeout_check (Wm *w); static void wm_sn_exec(Wm *w, char* name, char* bin_name, char *desc); static void wm_sn_monitor_event_func (SnMonitorEvent *event, void *user_data); static void wm_sn_cycle_update_root_prop(Wm *w); static SnCycle *wm_sn_cycle_new(Wm *w, const char *bin_name); static void wm_sn_cycle_add(Wm *w, const char *bin_name); #endif Wm* wm_new(int argc, char **argv) { XSetWindowAttributes sattr; /* for root win */ Wm *w = NULL; XColor dummy_col; w = malloc(sizeof(Wm)); memset(w, 0, sizeof(Wm)); w->flags = STARTUP_FLAG; wm_load_config(w, &argc, argv); XSetErrorHandler(handle_xerror); w->screen = DefaultScreen(w->dpy); w->root = RootWindow(w->dpy, w->screen); w->dpy_width = DisplayWidth(w->dpy, w->screen); w->dpy_height = DisplayHeight(w->dpy, w->screen); w->n_active_ping_clients = 0; w->next_click_is_not_double = True; #ifdef USE_SM sm_connect(w); /* previous_cliend_id */ #endif sattr.event_mask = SubstructureRedirectMask |SubstructureNotifyMask |StructureNotifyMask |PropertyChangeMask; /* Tell root win we wanna be wm */ XChangeWindowAttributes(w->dpy, w->root, CWEventMask, &sattr); XSelectInput(w->dpy, w->root, sattr.event_mask); /* Use this 'dull' color for 'base' window backgrounds and such. 'Appears' to actually reduce flicker */ XAllocNamedColor(w->dpy, DefaultColormap(w->dpy, w->screen), "grey", &w->grey_col, &dummy_col); #ifdef USE_GCONF g_type_init() ; w->gconf_client = gconf_client_get_default(); w->gconf_context = g_main_context_default (); if (w->gconf_client != NULL) { gconf_client_add_dir(w->gconf_client, "/apps/matchbox", /* GCONF_CLIENT_PRELOAD_NONE */ GCONF_CLIENT_PRELOAD_RECURSIVE, NULL); gconf_client_notify_add(w->gconf_client, "/apps/matchbox", gconf_key_changed_callback, w, NULL, NULL); } else fprintf(stderr, "matchbox: failed to initialise gconf client\n"); #endif #ifdef USE_XSETTINGS w->xsettings_client = xsettings_client_new (w->dpy, w->screen, wm_xsettings_notify_cb, NULL, (void *)w ); #endif #ifndef STANDALONE w->pb = mb_pixbuf_new(w->dpy, w->screen); if (w->config->use_icons) { w->img_generic_icon = mb_pixbuf_img_new_from_file(w->pb, GENERIC_ICON); if (w->img_generic_icon == NULL) { fprintf(stderr,"matchbox: WARNING: failed to load %s . Disabling icons.\n", GENERIC_ICON); w->config->use_icons = 0; } else misc_scale_wm_app_icon(w); } #endif #ifndef NO_KBD keys_init(w); #endif ewmh_init(w); comp_engine_init (w); mbtheme_init(w, w->config->theme); ewmh_init_props(w); wm_set_cursor_visibility(w, !w->config->no_cursor); w->curs_busy = XCreateFontCursor(w->dpy, XC_watch); w->curs_drag = XCreateFontCursor(w->dpy, XC_fleur); #ifdef USE_LIBSN w->sn_display = sn_display_new (w->dpy, NULL, NULL); /*XXX error callbacks*/ w->sn_context = sn_monitor_context_new (w->sn_display, DefaultScreen (w->dpy), wm_sn_monitor_event_func, (void *)w, NULL); w->sn_busy_cnt = 0; w->sn_cycles = NULL; w->sn_mapping_list = NULL; #endif /* Panel/Dock in titlebar stuff */ w->have_titlebar_panel = NULL; w->flags ^= STARTUP_FLAG; /* Remove startup flag */ return w; } void wm_usage(char *progname) { printf("usage: %s [options ...]\n", progname); printf("\t-display \n"); printf("\t-theme \n"); printf("\t-use_titlebar \n"); printf("\t-use_cursor \n"); #ifndef USE_COMPOSITE printf("\t-use_lowlight \n"); #endif printf("\t-use_dialog_mode \n"); printf("\t-use_desktop_mode \n"); printf("\t-use_super_modal \n"); printf("\t-force_dialogs \n"); #ifndef NO_KBD printf("\t-kbdconfig \n"); #endif #ifdef USE_SM printf("\t--sm-client-id \n"); #endif #ifdef STANDALONE printf("\t-titlebar_panel \n"); #endif printf("\n"); printf("Compile time features;\n"); #ifdef DEBUG printf("\tdebug build yes\n"); #else printf("\tdebug build no\n"); #endif #if defined(USE_XFT) || defined(MB_HAVE_XFT) printf("\tXFT support yes\n"); #else printf("\tXFT support no\n"); #endif #ifdef USE_LIBSN printf("\tStartup Notification support yes\n"); #else printf("\tStartup Notification support no\n"); #endif #ifdef USE_EXPAT printf("\tExpat support yes\n"); #else printf("\tExpat support no\n"); #endif #ifdef USE_XSETTINGS printf("\tXSettings support yes\n"); #else printf("\tXSettings support no\n"); #endif #ifdef MB_HAVE_PNG printf("\tPNG support yes\n"); #else printf("\tPNG support no\n"); #endif #ifdef MB_HAVE_JPEG printf("\tJPG support yes\n"); #else printf("\tJPG support no\n"); #endif #ifdef MB_HAVE_PANGO printf("\tpango support yes\n"); #else printf("\tpango support no\n"); #endif #ifndef STANDALONE printf("\tTheme support yes\n"); #else printf("\tTheme support no\n"); #endif #ifdef USE_GCONF printf("\tgconf support yes\n"); #else printf("\tgconf support no\n"); #endif #ifdef USE_COMPOSITE printf("\tcomposite support yes\n"); #else printf("\tcomposite support no\n"); #endif #ifndef NO_PING printf("\tping protocol support yes\n"); #else printf("\tping protocol support no\n"); #endif #ifndef NO_XRM printf("\tXRM support yes\n"); #else printf("\tXRM support no\n"); #endif #ifdef USE_SM printf("\tSession support yes\n"); #else printf("\tSession support no\n"); #endif printf("\nmatchbox-window-manager %s \nCopyright (C) 2005 Matthew Allum, OpenedHand Ltd.\n", VERSION); printf("This is free software; see the source for copying conditions.\n"); printf("Visit http://projects.o-hand.com/matchbox for more info.\n"); exit(0); } #ifdef NO_XRM void wm_load_config (Wm *w, int *argc, char *argv[]) { int i; /* Configure these now, and let the arguments override them */ w->config = malloc(sizeof(Wm_config)); memset(w->config, 0, sizeof(Wm_config)); /* config defaults */ w->config->use_title = True; w->config->display_name[0] = '\0'; w->config->dbl_click_time = 200; w->config->use_icons = 16; w->config->no_cursor = False; w->config->dialog_shade = False; w->config->dialog_stratergy = WM_DIALOGS_STRATERGY_CONSTRAINED; w->config->ping_handler = getenv("MB_HUNG_APP_HANDLER"); w->config->ping_aggressive = getenv("MB_AGGRESSIVE_PING") ? True : False; #ifdef USE_COMPOSITE w->config->dialog_shade = True; #endif w->config->lowlight_params[0] = 0; w->config->lowlight_params[1] = 0; w->config->lowlight_params[2] = 0; w->config->lowlight_params[3] = 100; if (getenv("DISPLAY") != NULL) strcpy(w->config->display_name, (char *)getenv("DISPLAY")); for (i = 1; i < *argc; i++) { if (!strcmp ("-theme", argv[i])) { if (++i>=*argc) wm_usage (argv[0]); #ifdef STANDALONE fprintf(stderr, "matchbox: This matchbox build does not support themeing\n"); #else w->config->theme = argv[i]; dbg("%s() got theme :%s ", __func__, w->config->theme); #endif continue; } if (!strcmp ("-use_titlebar", argv[i])) { if (++i>=*argc) wm_usage (argv[0]); if (strcmp(argv[i], "no") == 0) w->config->use_title = False; continue; } if (!strcmp("-display", argv[i])) { if (++i>=*argc) wm_usage (argv[0]); strcpy (w->config->display_name, argv[i]); continue; } if (!strcmp ("-use_cursor", argv[i])) { if (++i>=*argc) wm_usage (argv[0]); if (strcmp(argv[i], "no") == 0) w->config->no_cursor = True; continue; } #ifndef USE_COMPOSITE if (!strcmp ("-use_lowlight", argv[i])) { if (++i>=*argc) wm_usage (argv[0]); if (strcmp(argv[i], "yes") == 0) w->config->dialog_shade = True; continue; } #endif if (!strcmp ("-use_super_modal", argv[i])) { if (++i>=*argc) wm_usage (argv[0]); if (strcmp(argv[i], "yes") == 0) w->config->super_modal = True; continue; } if (!strcmp("-use_dialog_mode", argv[i])) { if (++i>=*argc) wm_usage (argv[0]); if (!strcmp("free", argv[i])) { w->config->dialog_stratergy = WM_DIALOGS_STRATERGY_FREE; } else if (!strcmp("const-horiz", argv[i])) { w->config->dialog_stratergy = WM_DIALOGS_STRATERGY_CONSTRAINED_HORIZ; } else if (!strcmp("static", argv[i])) { w->config->dialog_stratergy = WM_DIALOGS_STRATERGY_STATIC; } else wm_usage (argv[0]); continue; } if (!strcmp("-use_desktop_mode", argv[i])) { if (++i>=*argc) wm_usage (argv[0]); if (!strcmp("decorated", argv[i])) w->flags |= DESKTOP_DECOR_FLAG; continue; } #ifdef STANDALONE if (!strcmp("-titlebar_panel", argv[i])) { int flags = 0; if (++i>=*argc) wm_usage (argv[0]); flags = XParseGeometry(argv[i], &w->toolbar_panel_x, &w->toolbar_panel_y, &w->toolbar_panel_w, &w->toolbar_panel_h) ; if ((flags & XValue) && (flags & YValue) && (flags & WidthValue) && (flags & HeightValue)) w->have_toolbar_panel = True; else fprintf(stderr, "matchbox: titlebar panel geometry string invalid\n"); continue; } #endif if (!strcmp ("-force_dialogs", argv[i])) { if (++i>=*argc) wm_usage (argv[0]); w->config->force_dialogs = argv[i]; continue; } #ifndef NO_KBD if (!strcmp ("-kbdconfig", argv[i])) { if (++i>=*argc) wm_usage (argv[0]); w->config->kbd_conf_file = argv[i]; continue; } #endif #ifdef USE_SM if (!strcmp ("--sm-client-id", argv[i])) { if (++i>=*argc) wm_usage (argv[0]); w->config->sm_client_id = strdup(argv[i]); continue; } #endif wm_usage (argv[0]); } if ((w->dpy = XOpenDisplay(w->config->display_name)) == NULL) { fprintf(stderr, "matchbox-wm: can't open display! check your DISPLAY variable.\n"); exit(1); } } #else /* We have memory hungry XRM */ void wm_load_config (Wm *w, int *argc, char *argv[]) { static XrmDatabase rDB, cmdlnDB, srDB; char *type; XrmValue value; static int opTableEntries = 12; static XrmOptionDescRec opTable[] = { {"-theme", ".theme", XrmoptionSepArg, (XPointer) NULL}, {"-use_titlebar",".titlebar", XrmoptionSepArg, (XPointer) NULL}, {"-display", ".display", XrmoptionSepArg, (XPointer) NULL}, {"-use_cursor", ".cursor", XrmoptionSepArg, (XPointer) NULL}, {"-use_lowlight", ".lowlight", XrmoptionSepArg, (XPointer) NULL}, {"-use_dialog_mode", ".dialog", XrmoptionSepArg, (XPointer) NULL}, {"-use_desktop_mode",".desktop", XrmoptionSepArg, (XPointer) NULL}, {"-titlebar_panel", ".titlebarpanel", XrmoptionSepArg, (XPointer) NULL}, {"-force_dialogs", ".forcedialogs", XrmoptionSepArg, (XPointer) NULL}, {"--sm-client-id", ".session", XrmoptionSepArg, (XPointer) NULL}, {"-use_super_modal", ".supermodal", XrmoptionSepArg, (XPointer) NULL}, {"-kbdconfig", ".kbdconfig", XrmoptionSepArg, (XPointer) NULL}, }; XrmInitialize(); rDB = XrmGetFileDatabase(CONFDEFAULTS); XrmParseCommand(&cmdlnDB, opTable, opTableEntries, "matchbox", argc, argv); if (*argc != 1) wm_usage(argv[0]); XrmCombineDatabase(cmdlnDB, &rDB, True); w->config = malloc(sizeof(Wm_config)); memset(w->config, 0, sizeof(Wm_config)); /* config defaults */ w->config->use_title = True; w->config->display_name[0] = '\0'; w->config->dbl_click_time = 200; w->config->use_icons = 16; w->config->no_cursor = False; w->config->dialog_shade = False; w->config->dialog_stratergy = WM_DIALOGS_STRATERGY_CONSTRAINED; w->config->ping_handler = getenv("MB_HUNG_APP_HANDLER"); w->config->ping_aggressive = getenv("MB_AGGRESSIVE_PING") ? True : False; if (XrmGetResource(rDB, "matchbox.display", "Matchbox.Display", &type, &value) == True) { strncpy(w->config->display_name, value.addr, (int) value.size); w->config->display_name[value.size] = '\0'; } else { if (getenv("DISPLAY") != NULL) strcpy(w->config->display_name, (char *)getenv("DISPLAY")); } if ((w->dpy = XOpenDisplay(w->config->display_name)) == NULL) { fprintf(stderr, "matchbox: can't open display! check your DISPLAY variable.\n"); exit(1); } if (XResourceManagerString(w->dpy) != NULL) { srDB = XrmGetStringDatabase(XResourceManagerString(w->dpy)); if (srDB) XrmCombineDatabase(srDB, &rDB, False); } if (XrmGetResource(rDB, "matchbox.theme", "Matchbox.Theme", &type, &value) == True) { #ifdef STANDALONE fprintf(stderr, "matchbox: This matchbox build does not support themeing\n"); #else w->config->theme = (char *)malloc(sizeof(char)*(value.size+1)); strncpy(w->config->theme, value.addr, (int) value.size); w->config->theme[value.size] = '\0'; dbg("%s() got theme :%s ", __func__, w->config->theme); #endif } else { w->config->theme = NULL; } if (XrmGetResource(rDB, "matchbox.forcedialogs", "Matchbox.ForceDialogs", &type, &value) == True) { w->config->force_dialogs = (char *)malloc(sizeof(char)*(value.size+1)); strncpy(w->config->force_dialogs, value.addr, (int) value.size); w->config->force_dialogs[value.size] = '\0'; dbg("%s() got force dialogs :%s ", __func__, w->config->force_dialogs); } #ifndef NO_KBD if (XrmGetResource(rDB, "matchbox.kbdconfig", "Matchbox.Keybdconfig", &type, &value) == True) { w->config->kbd_conf_file = (char *)malloc(sizeof(char)*(value.size+1)); strncpy(w->config->kbd_conf_file, value.addr, (int) value.size); w->config->kbd_conf_file[value.size] = '\0'; dbg("%s() got kbdconfig :%s ", __func__, w->config->kbd_conf_file); } #endif if (XrmGetResource(rDB, "matchbox.titlebar", "Matchbox.Titlebar", &type, &value) == True) { if(strncmp(value.addr, "no", (int) value.size) == 0) { dbg("%s() TURNING TITLE OFF\n", __func__); w->config->use_title = False; } } if (XrmGetResource (rDB, "matchbox.cursor", "Matchbox.Cursor", &type, &value) == True) { if (strncmp (value.addr, "no", (int) value.size) == 0) { dbg("%s() TURNING CURSOR OFF\n", __func__); w->config->no_cursor = True; } } if (XrmGetResource(rDB, "matchbox.supermodal", "Matchbox.Supermodal", &type, &value) == True) { if(strncmp(value.addr, "yes", (int) value.size) == 0) { dbg("%s() TURNING SUPER MODAL ON\n", __func__); w->config->super_modal = True; } } /* * Composite matchbox always uses lowlighting */ #ifndef USE_COMPOSITE if (XrmGetResource (rDB, "matchbox.lowlight", "Matchbox.Lowlight", &type, &value) == True) { if (strncmp (value.addr, "yes", (int) value.size) == 0) { #endif dbg("%s() TURNING LOWLIGHT ON\n", __func__); w->config->dialog_shade = True; /* values below now set in theme */ w->config->lowlight_params[0] = 0; w->config->lowlight_params[1] = 0; w->config->lowlight_params[2] = 0; w->config->lowlight_params[3] = 100; #ifndef USE_COMPOSITE } } #endif if (XrmGetResource (rDB, "matchbox.dialog", "Matchbox.Dialog", &type, &value) == True) { if (strncmp (value.addr, "free", (int) value.size) == 0) { w->config->dialog_stratergy = WM_DIALOGS_STRATERGY_FREE; } else if (strncmp (value.addr, "const-horiz", (int) value.size) == 0) { w->config->dialog_stratergy = WM_DIALOGS_STRATERGY_CONSTRAINED_HORIZ; } else if (strncmp (value.addr, "static", (int) value.size) == 0) { w->config->dialog_stratergy = WM_DIALOGS_STRATERGY_STATIC; } else wm_usage("matchbox"); } if (XrmGetResource (rDB, "matchbox.desktop", "Matchbox.Desktop", &type, &value) == True) { if (strncmp (value.addr, "decorated", (int) value.size) == 0) { w->flags |= DESKTOP_DECOR_FLAG; } } #ifdef STANDALONE if (XrmGetResource(rDB, "matchbox.titlebarpanel", "Matchbox.Titlebarpanel", &type, &value) == True) { int flags = 0; char *geom = (char *)malloc(sizeof(char)*(value.size+1)); strncpy(geom, value.addr, (int) value.size); geom[value.size] = '\0'; flags = XParseGeometry(geom, &w->toolbar_panel_x, &w->toolbar_panel_y, &w->toolbar_panel_w, &w->toolbar_panel_h) ; if ((flags & XValue) && (flags & YValue) && (flags & WidthValue) && (flags & HeightValue)) w->have_toolbar_panel = True; else fprintf(stderr, "matchbox: titlebar panel geometry string invalid\n"); free(geom); } #endif #ifdef USE_SM if (XrmGetResource(rDB, "matchbox.session", "Matchbox.session", &type, &value) == True) { w->config->sm_client_id = (char *)malloc(sizeof(char)*(value.size+1)); strncpy(w->config->sm_client_id, value.addr, (int) value.size); w->config->sm_client_id[value.size] = '\0'; dbg("%s() got sm_client_id: %s ", __func__, w->config->sm_client_id); } #endif } #endif /* NO_XRM */ void wm_init_existing(Wm *w) { unsigned int nwins, i; Window dummyw1, dummyw2, *wins; XWindowAttributes attr; Client *c; /* set blank hints */ ewmh_update_rects(w); ewmh_update_lists(w); ewmh_update_desktop_hint(w); XQueryTree(w->dpy, w->root, &dummyw1, &dummyw2, &wins, &nwins); for (i = 0; i < nwins; i++) { XGetWindowAttributes(w->dpy, wins[i], &attr); if (!attr.override_redirect && attr.map_state == IsViewable) { c = wm_make_new_client(w, wins[i]); #ifdef USE_COMPOSITE if (c) c->ignore_unmap = 2; /* comp seems to unmap twice ? */ #else if (c) c->ignore_unmap++; #endif } } XFree(wins); } Client* wm_find_client(Wm *w, Window win, int mode) { Client *c = NULL; if (stack_empty(w)) return NULL; if (mode == FRAME) { stack_enumerate_reverse(w, c) if (c->frame == win || (client_title_frame(c) && client_title_frame(c) == win) ) return c; } else { stack_enumerate_reverse(w, c) if (c->window == win) return c; } return NULL; } /* Grab an X Event - block but With a timeout */ static Bool get_xevent_timed(Wm *w, XEvent *event_return, struct timeval *tv) { if (tv->tv_usec == 0 && tv->tv_sec == 0) { #ifndef USE_SM XNextEvent(w->dpy, event_return); return True; #else tv->tv_sec = 5; #endif } XFlush(w->dpy); if (XPending(w->dpy) == 0) { int fd = ConnectionNumber(w->dpy); fd_set readset; FD_ZERO(&readset); FD_SET(fd, &readset); #ifdef USE_SM if (w->sm_ice_fd != -1) { FD_SET(w->sm_ice_fd, &readset); if (w->sm_ice_fd > fd) fd = w->sm_ice_fd; /* for +1 below */ } #endif if (select(fd+1, &readset, NULL, NULL, tv) == 0) { return False; } else { #ifdef USE_SM if ( w->sm_ice_fd != -1 && FD_ISSET(w->sm_ice_fd, &readset) ) { sm_process_event(w); return False; } #endif XNextEvent(w->dpy, event_return); return True; } } else { XNextEvent(w->dpy, event_return); return True; } } #ifdef USE_COMPOSITE /* For the compositing engine we need to track overide redirect * windows so the compositor can paint them. * * What we do is make a 'lightweight' client object. * * TODO: base_client_new() should really be able to do this. * and avoid any extra code. */ void wm_handle_map_notify(Wm *w, Window win) { XWindowAttributes attr; Client *new_client = NULL; /* Do we already have it ? */ if (wm_find_client(w, win, WINDOW)) return; if (wm_find_client(w, win, FRAME)) return; dbg("%s() called for unknown window\n", __func__); misc_trap_xerrors(); XGetWindowAttributes(w->dpy, win, &attr); if (misc_untrap_xerrors()) return; /* safety on */ if (attr.class == InputOnly) return; if (attr.override_redirect) { dbg("%s() making new overide redirect window\n", __func__); new_client = malloc(sizeof(Client)); memset(new_client, 0, sizeof(Client)); new_client->x = attr.x; new_client->y = attr.y; new_client->width = attr.width; new_client->height = attr.height; new_client->visual = attr.visual; new_client->want_shadow = True; new_client->type = MBCLIENT_TYPE_OVERRIDE; new_client->frame = new_client->window = win; new_client->mapped = True; new_client->name = strdup(""); new_client->wm = w; /* Set up the 'methods' - expect to be overidden */ base_client_set_funcs(new_client); stack_append_top(new_client); dbg("%s() client frame is %li\n", __func__, new_client->frame); comp_engine_client_init(w, new_client); comp_engine_client_show(w, new_client); } } #endif /* Main event loop, timeout for polling stuff */ void wm_event_loop(Wm* w) { XEvent ev; int hung_app_timer = 0; struct timeval tvt; for (;;) { tvt.tv_usec = 0; tvt.tv_sec = 0; #ifdef USE_LIBSN if (w->sn_busy_cnt) tvt.tv_sec = 1; #endif #ifdef USE_GCONF if (w->gconf_client != NULL) tvt.tv_sec = 1; #endif #ifndef NO_PING if (w->n_active_ping_clients) tvt.tv_sec = 1; #endif if (get_xevent_timed(w, &ev, &tvt)) { switch (ev.type) { #ifdef USE_COMPOSITE case MapNotify: wm_handle_map_notify(w, ev.xmap.window); break; #endif case ButtonPress: wm_handle_button_event(w, &ev.xbutton); break; case MapRequest: wm_handle_map_request(w, &ev.xmaprequest); break; case UnmapNotify: wm_handle_unmap_event(w, &ev.xunmap); break; case Expose: wm_handle_expose_event(w, &ev.xexpose); break; case DestroyNotify: wm_handle_destroy_event(w, &ev.xdestroywindow); break; case ConfigureRequest: wm_handle_configure_request(w, &ev.xconfigurerequest); break; case ConfigureNotify: wm_handle_configure_notify(w, &ev.xconfigure); break; case ClientMessage: wm_handle_client_message(w, &ev.xclient); break; case KeyPress: wm_handle_keypress(w, &ev.xkey); break; case PropertyNotify: wm_handle_property_change(w, &ev.xproperty); break; case GravityNotify: dbg("**** got gravity event ***"); break; #ifndef NO_KBD case MappingNotify: dbg("%s() got MappingNotify\n", __func__); XRefreshKeyboardMapping(&ev.xmapping); break; #endif default: dbg("%s() ignoring event->type : %d\n", __func__, ev.type); break; } comp_engine_handle_events(w, &ev); #ifdef USE_XSYNC if (w->have_xsync && ev.type == w->sync_event_base + XSyncAlarmNotify) { dbg("%s() got ewmh_sync alarm notify\n", __func__); ewmh_sync_handle_event(w, (XSyncAlarmNotifyEvent*)&ev); } #endif #ifdef USE_XSETTINGS if (w->xsettings_client != NULL) xsettings_client_process_event(w->xsettings_client, &ev); #endif #ifdef USE_LIBSN sn_display_process_event (w->sn_display, &ev); #endif } else { /* No X event poll checks here */ #ifdef USE_LIBSN if (w->sn_busy_cnt) wm_sn_timeout_check (w); #endif #ifdef USE_GCONF if (w->gconf_client != NULL) g_main_context_iteration (w->gconf_context, FALSE); #endif #ifndef NO_PING /* check for hung apps every PING_CHECK_FREQ seconds */ if (++hung_app_timer >= PING_CHECK_FREQ && w->n_active_ping_clients) { hung_app_timer = 0; ewmh_hung_app_check(w); } #endif } #ifdef USE_COMPOSITE if (w->all_damage) { comp_engine_render(w, w->all_damage); XFixesDestroyRegion (w->dpy, w->all_damage); w->all_damage = None; } #endif } } void wm_handle_button_event(Wm *w, XButtonEvent *e) { Client *p = NULL; Client *c = wm_find_client(w, e->window, WINDOW); dbg("%s() called\n", __func__); /* Raise dialogs, set focus if needed */ if (c && !(w->flags & MENU_FLAG)) { /* Click was on window rather than decorations */ /* Dont move focus for a modal dialog */ if (w->focused_client && c != w->focused_client && w->focused_client->type == MBCLIENT_TYPE_DIALOG && w->focused_client->trans == c && w->focused_client->flags & CLIENT_IS_MODAL_FLAG) { XAllowEvents(w->dpy, ReplayPointer, CurrentTime); return; } if (c->type == MBCLIENT_TYPE_DIALOG && w->config->dialog_stratergy != WM_DIALOGS_STRATERGY_STATIC) { /* raise the dialog up, handle focus etc */ if (c != w->focused_client) wm_activate_client(c); } else { client_set_focus(c); if (c->type & (MBCLIENT_TYPE_DESKTOP|MBCLIENT_TYPE_APP)) c->next_focused_client = NULL; } /* forward grabbed events */ XAllowEvents(w->dpy, ReplayPointer, CurrentTime); return; } c = wm_find_client(w, e->window, FRAME); /* remove task menu if its up */ if (w->flags & MENU_FLAG) { if (c && c->type == MBCLIENT_TYPE_TASK_MENU ) c->button_press(c,e); stack_enumerate(w, p) if (p->type == MBCLIENT_TYPE_TASK_MENU) { select_client_destroy(p); break; } return; } /* Pass the event on to the window class */ if (c) c->button_press(c,e); } void wm_handle_keypress(Wm *w, XKeyEvent *e) { #ifndef NO_KBD MBConfigKbdEntry *entry = w->config->kb->entrys; Client *p = NULL; int state = e->state; #ifdef USE_LIBSN Bool found = False; SnCycle *current_cycle = w->sn_cycles; #endif dbg("%s() called\n", __func__ ); if(w->flags & MENU_FLAG) { stack_enumerate(w, p) if ( p->type == MBCLIENT_TYPE_TASK_MENU) break; if (p->type == MBCLIENT_TYPE_TASK_MENU) { select_client_event_loop( p, NULL ); select_client_destroy (p); return; } } /* Don't care about Caps/Num/Scroll lock here */ state &= ~w->config->kb->lock_mask; while (entry != NULL) { if (XKeycodeToKeysym(w->dpy, e->keycode, entry->index) == entry->key && state == entry->ModifierMask ) { switch (entry->action) { case KEY_ACTN_EXEC: fork_exec(entry->sdata); break; #ifdef USE_LIBSN case KEY_ACTN_EXEC_SINGLE: if (current_cycle != NULL) { while(current_cycle != NULL) { dbg("%s(): checking %s\n", __func__, current_cycle->bin_name); if (!strcmp(current_cycle->bin_name, entry->sdata) && current_cycle->xid == None) { dbg("%s() %s is already starting\n", __func__, entry->sdata); return; /* entry is in process of starting */ } current_cycle = current_cycle->next; } } current_cycle = w->sn_cycles; if (!stack_empty(w)) { while(current_cycle != NULL) { if (current_cycle->xid != None && !strcmp(current_cycle->bin_name, entry->sdata)) { p = wm_find_client(w, current_cycle->xid, WINDOW); if (p) { wm_activate_client(p); found = True; } } current_cycle = current_cycle->next; } } if (!found) wm_sn_exec(w, entry->sdata, entry->sdata, entry->sdata); break; case KEY_ACTN_EXEC_SN: wm_sn_exec(w, entry->sdata, entry->sdata, entry->sdata); break; #endif case KEY_ACTN_NEXT_CLIENT: wm_activate_client(stack_cycle_backward(w, MBCLIENT_TYPE_APP)); break; case KEY_ACTN_PREV_CLIENT: wm_activate_client(stack_cycle_forward(w, MBCLIENT_TYPE_APP)); break; case KEY_ACTN_CLOSE_CLIENT: if (w->stack_top_app) client_deliver_delete(w->stack_top_app); break; case KEY_ACTN_TOGGLE_DESKTOP: wm_toggle_desktop(w); break; case KEY_ACTN_TASK_MENU_ACTIVATE: select_client_new(w); break; case KEY_ACTN_HIDE_TITLEBAR: if (w->stack_top_app) main_client_toggle_title_bar(w->stack_top_app); break; case KEY_ACTN_FULLSCREEN: if (w->stack_top_app) main_client_toggle_fullscreen(w->stack_top_app); break; } } entry = entry->next_entry; } #endif } void wm_handle_configure_notify(Wm *w, XConfigureEvent *e) { Client *p, *cdesktop = NULL; Client *ctitledock = NULL; int height_diff, width_diff; dbg("%s() called\n", __func__); if (e->window == w->root) /* screen rotation */ { /* TODO: * It would probably be cleaner ( though add a dep ) * to use randr here to get more info about the rotation * ( eg. direction ) */ dbg("%s() configure notify event called on root", __func__ ); if (e->width != w->dpy_width || e->height != w->dpy_height) { height_diff = e->height - w->dpy_height; width_diff = e->width - w->dpy_width; w->dpy_width = e->width; w->dpy_height = e->height; if (stack_empty(w)) return; XGrabServer(w->dpy); /* Clear all caches so everything gets redrawn ok */ theme_img_cache_clear_all( w->mbtheme ); theme_pixmap_cache_clear_all( w->mbtheme ); stack_enumerate(w, p) { switch (p->type) { case MBCLIENT_TYPE_APP : p->width += width_diff; p->height += height_diff; p->have_cache = False; break; case MBCLIENT_TYPE_TOOLBAR : p->width += width_diff; p->y += height_diff; break; case MBCLIENT_TYPE_PANEL : if (p->flags & CLIENT_DOCK_WEST) { p->height += height_diff; } else if (p->flags & CLIENT_DOCK_EAST) { p->height += height_diff; p->x += width_diff; } else if (p->flags & CLIENT_DOCK_SOUTH) { p->width += width_diff; p->y += height_diff; } else if (p->flags & CLIENT_DOCK_NORTH) { p->width += width_diff; } else if (p->flags & CLIENT_DOCK_TITLEBAR) { ctitledock = p; } break; case MBCLIENT_TYPE_DIALOG : /* * TODO: * show check if the dialog is centered and make sure * if gets recentered on rotation ? * * - Change x,y,width?,height? rotation size change factors ? * - above may 'just work' * - Set x=0, y=0 so centering is forced * - set a flag is dialog if initally centered ? */ dialog_client_configure(p); break; case MBCLIENT_TYPE_DESKTOP: p->width += width_diff; p->height += height_diff; cdesktop = p; break; default: break; } /* we leave desktop/titlebar dock till last */ if (p != cdesktop && p != ctitledock) { p->move_resize(p); /* destroy buttons so they get reposioned */ client_buttons_delete_all(p); p->redraw(p, False); client_deliver_config(p); } comp_engine_client_repair (w, p); } if (cdesktop) { cdesktop->move_resize(cdesktop); client_deliver_config(cdesktop); } if (ctitledock) { dockbar_client_configure(ctitledock); ctitledock->move_resize(ctitledock); client_deliver_config(ctitledock); } comp_engine_destroy_root_buffer(w); comp_engine_render(w, None); ewmh_update_rects(w); wm_activate_client(wm_get_visible_main_client(w)); XSync(w->dpy, False); XUngrabServer(w->dpy); } } } void wm_handle_configure_request (Wm *w, XConfigureRequestEvent *e ) { Client *c = wm_find_client(w, e->window, WINDOW); XWindowChanges xwc; unsigned long value_mask = e->value_mask; int req_x = e->x; int req_y = e->y; int req_w = e->width; int req_h = e->height; Bool want_activate = False; /* TODO: Double check ICCCM on this code. * see http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.5 * Only send synthetic configure notify on no change. * */ if (!c ) { /* Dont know any thing about this window. Let it have what it * wants and leave. * Can be a window initally mapping, thus the trap as it could * of already dissapeared. */ dbg("%s() could find source client %ix%i\n", __func__, e->width, e->height); xwc.x = req_x; xwc.y = req_y; xwc.width = req_w; xwc.height = req_h; xwc.sibling = e->above; xwc.stack_mode = e->detail; misc_trap_xerrors(); XConfigureWindow(w->dpy, e->window, e->value_mask, &xwc); misc_untrap_xerrors(); return; } /* Process exactly what changes have been reuested */ if (!(value_mask & CWWidth)) req_w = c->width; if (!(value_mask & CWHeight)) req_h = c->height; if ((e->x <= 0 && !(c->flags & CLIENT_TITLE_HIDDEN_FLAG)) || !(value_mask & CWX)) req_x = c->x; if ((e->y <= 0 && !(c->flags & CLIENT_TITLE_HIDDEN_FLAG)) || !(value_mask & CWY)) req_y = c->y; dbg("%s() for win %s - have w: %i vs %i, h: %i" "vs %i, x: %i vs %i, y: %i vs %i,\n", __func__, c->name, c->width, req_w, c->height, req_h, c->x, req_x, c->y, req_y ); /* Deal with raising - needs work, not sure if anything really * relies on this / or how it fits with mb. * * Wins can use _NET_ACTIVATE_WINDOW to raise themselse. * Dialogs should be able to raise / lower theme selves ? */ if (value_mask & (CWSibling|CWStackMode)) { /* e->above is sibling * e->detail is stack_mode */ Client *sibling = wm_find_client(w, e->window, WINDOW); dbg("%s() value_mask & (CWSibling|CWStackMode) sibling is %s (%li vs %li\n", __func__, sibling ? sibling->name : "unkown", e->window, c->window ); if (sibling) { switch (e->detail) { case Above: dbg("%s() (CWSibling|CWStackMode) above %s\n", __func__, sibling->name); /* Dialog 'above its self, call activate to raise it */ if (sibling == c && c->type == MBCLIENT_TYPE_DIALOG) want_activate = True; break; case Below: dbg("%s() (CWSibling|CWStackMode) below %s\n", __func__, sibling->name); /* Unsupported currently clear flags */ value_mask &= ~(CWSibling|CWStackMode); break; case TopIf: /* What do these mean ? */ case BottomIf: case Opposite: default: dbg("%s() (CWSibling|CWStackMode) uh? %s\n", __func__, sibling->name); /* Unsupported currently clear flags */ value_mask &= ~(CWSibling|CWStackMode); break; } } else /* Just clear the flags now to be safe */ value_mask &= ~(CWSibling|CWStackMode); } if (c->type == MBCLIENT_TYPE_PANEL) /* Panels can move */ { if ( c->height != req_h || c->width != req_w || c->x != req_x || c->y != req_y ) { Window win_tmp = c->window; xwc.width = e->width; xwc.height = e->height; xwc.x = e->x; xwc.y = e->y; xwc.border_width = 0; xwc.sibling = 0; xwc.stack_mode = e->detail; XConfigureWindow(w->dpy, e->window, value_mask, &xwc); client_deliver_config(c); /* TODO: Not needed */ client_set_state(c, WithdrawnState); /* Now we destroy the window internally and re-birth it. * A bit of a cheat, but works. */ XReparentWindow(w->dpy, c->window, w->root, e->x, e->y); c->destroy(c); c = wm_make_new_client(w, win_tmp); if (c) c->ignore_unmap++; return; } } if (c->type == MBCLIENT_TYPE_TOOLBAR) /* can change height */ { if (req_h != c->height && !(c->flags & CLIENT_IS_MINIMIZED)) { int change_amount = c->height - e->height; c->y += change_amount; c->height = e->height; c->move_resize(c); wm_update_layout(w, c, change_amount); return; } } if (c->type == MBCLIENT_TYPE_DIALOG) { /* Update the size the dialog is trying to get too eventually * - eg toolbar/panel/input windows may dissapear and make * more space available. */ if (e->width && (value_mask & CWWidth) && e->width != c->width && e->width != c->init_width) c->init_width = e->width; if (e->height && (value_mask & CWHeight) && e->height != c->height && e->height != c->init_height) c->init_height = e->height; /* If any changes, now make them fit it into avaialable area. */ if (req_x != c->x || req_y != c->y || req_w != c->width || req_h != c->height || want_activate) { Bool want_fake_configure = False; if (req_x == c->x && req_y == c->y && req_w != c->width && req_h != c->height) { switch (c->gravity) { case NorthEastGravity: req_x = c->x -= (req_w - c->width); break; case EastGravity: req_x = c->x -= (req_w - c->width); break; case SouthWestGravity: req_y = c->y -= (req_h - c->height); break; case SouthGravity: req_y = c->y -= (req_h - c->height); break; case SouthEastGravity: req_x = c->x -= (req_w - c->width); req_y = c->y -= (req_h - c->height); break; case CenterGravity: /* FIXME */ case NorthWestGravity: case NorthGravity: case WestGravity: case StaticGravity: default: break; } } dialog_constrain_geometry(c, &req_x, &req_y, &req_w, &req_h); #ifdef USE_ALT_INPUT_WIN /* Alternate Input windows use the dialog type to * fit in with stacking etc but they cant change width, x nor y */ if (c->flags & (CLIENT_TB_ALT_TRANS_FOR_DIALOG |CLIENT_TB_ALT_TRANS_FOR_APP)) { if (c->height != req_h) { Client *trans_client = c->trans; int diff = c->height - req_h; c->height = req_h; c->y += diff; toolbar_client_move_resize(c); if (trans_client) { if (trans_client->type == MBCLIENT_TYPE_DIALOG) { /* do not grow over their initial size because of IM resizing smaller */ if (trans_client->height + diff > trans_client->init_height) { trans_client->height = trans_client->init_height; } else if (diff < 0) { /* Input window has grown larger so push it * up but keep it on screen. */ int x, y, w, h; dialog_client_get_coverage(trans_client, &x, &y, &w, &h); if (c->y < y+h) { int amount = (y+h) - c->y; /* move upwards */ trans_client->y -= amount; /* but not over screen area */ if (y-amount < 0) { trans_client->height -= amount-y; trans_client->y += amount-y; } } } } else /* Only Application window */ if (trans_client->type != MBCLIENT_TYPE_DESKTOP) trans_client->height += diff; trans_client->move_resize(trans_client); trans_client->redraw(trans_client, False); } } return; } #endif if (c->width == req_w && c->height == req_h) want_fake_configure = True; /* make sure buttons get repositioned */ if (c->width != req_w) client_buttons_delete_all(c); xwc.width = c->width = req_w; xwc.height = c->height = req_h; xwc.x = c->x = req_x; xwc.y = c->y = req_y; /* ICCCM says if window only moved - not size change, * then we send a fake one too. */ if (want_fake_configure) client_deliver_config(c); /* Client wants to be raised to top of stack */ if (want_activate) { dbg("%s(): calling activate client\n", __func__); wm_activate_client(c); } dialog_client_move_resize(c); dialog_client_redraw(c, False); /* make sure composite does any needed updates */ comp_engine_client_configure(w, c); comp_engine_client_show(w, c); return; } } /* Nothing has changed but ICCCM still send synthetic config * Code should fall back to this. */ client_deliver_config(c); } void wm_handle_map_request(Wm *w, XMapRequestEvent *e) { Client *c = wm_find_client(w, e->window, WINDOW); if (!c) { wm_make_new_client(w, e->window); } else { dbg("%s() Honoring map request for %s\n", __func__, c->name); wm_activate_client(c); } } void wm_handle_unmap_event(Wm *w, XUnmapEvent *e) { Client *c = wm_find_client(w, e->window, WINDOW); if (!c) return; dbg("%s() for client %s\n", __func__, c->name); if (c->ignore_unmap) { c->ignore_unmap--; dbg("%s() ignoring .. \n", __func__ ); } else { if (!c->mapped) return; /* TODO: Check this */ wm_remove_client(w, c); } } void wm_handle_expose_event(Wm *w, XExposeEvent *e) { Client *c = wm_find_client(w, e->window, FRAME); if (c) { XEvent ev; /* Compress expose events */ while (XCheckTypedWindowEvent(w->dpy, e->window, Expose, &ev)); dbg("%s() for %s\n", __func__, c->name); /* redraw title from cache - prolly a no-op * * All image paints happen via win backgrounds (server handles exposes) * We probably dont even need func at all. */ c->redraw(c, True); } } void wm_handle_destroy_event(Wm *w, XDestroyWindowEvent *e) { Client *c = wm_find_client(w, e->window, WINDOW); if (!c) return; dbg("%s for %s\n", __func__, c->name); wm_remove_client(w, c); } void wm_handle_client_message(Wm *w, XClientMessageEvent *e) { Client *c = wm_find_client(w, e->window, WINDOW); dbg("%s() messgae type is %li\n", __func__, e->message_type); /* Handle messages from mbcontrol */ if (e->message_type == w->atoms[MB_COMMAND]) { dbg("%s() mb command requested %li\n", __func__, e->data.l[0] ); switch (e->data.l[0]) { #ifndef STANDALONE case MB_CMD_SET_THEME : { Atom realType; unsigned long n; unsigned long extra; int format; int status; char *value = NULL; dbg("%s() atempting to switch theme\n", __func__ ); status = XGetWindowProperty(w->dpy, w->root, w->atoms[_MB_THEME], 0L, 512L, False, AnyPropertyType, &realType, &format, &n, &extra, (unsigned char **) &value); if (status == Success && value != NULL) { dbg("%s() switching theme to %s\n", __func__, value); mbtheme_switch(w, value); } if (value) XFree(value); return; } #endif case MB_CMD_EXIT : exit(0); case MB_CMD_NEXT : wm_activate_client(stack_cycle_backward(w, MBCLIENT_TYPE_APP)); break; case MB_CMD_PREV : wm_activate_client(stack_cycle_forward(w, MBCLIENT_TYPE_APP)); break; case MB_CMD_DESKTOP : wm_toggle_desktop(w); break; case MB_CMD_MISC: /* This is used for random testing stuff */ /* comp_engine_deinit(w); */ #ifdef DEBUG /* comp_engine_time(w); Not used atm XXX DO_TIMINGS */ dbg("*** Toggling composite visual debugging ***\n"); w->flags ^= DEBUG_COMPOSITE_VISIBLE_FLAG; #endif break; #ifndef NO_KBD case MB_CMB_KEYS_RELOAD: keys_reinit(w); break; #endif #ifdef USE_COMPOSITE case MB_CMD_COMPOSITE: if (w->comp_engine_disabled) comp_engine_reinit(w); else comp_engine_deinit(w); break; #endif } return; } if (e->message_type == w->atoms[WM_CHANGE_STATE]) { dbg("%s() messagae type is change state\n", __func__ ); if (c && e->format == 32 && e->data.l[0] == IconicState) { c->iconize(c); } return; } /* Also handle any EWMH messages */ ewmh_handle_root_message(w, e); } void wm_handle_property_change(Wm *w, XPropertyEvent *e) { Bool update_titlebar = False; Client *c = wm_find_client(w, e->window, WINDOW); if (!c) return; if (c->type == MBCLIENT_TYPE_OVERRIDE) return; #ifdef DEBUG { char *atomname = XGetAtomName(w->dpy, e->atom); dbg("%s() on %s, atom is %li ( %s )\n", __func__, c->name, e->atom, atomname ); if (atomname) free(atomname); } #endif /* Window Name changes */ if (e->atom == XA_WM_NAME && !c->name_is_utf8) { if (c->name) XFree(c->name); misc_trap_xerrors(); XFetchName(w->dpy, c->window, (char **)&c->name); if (!misc_untrap_xerrors()) { base_client_process_name(c); dbg("%s() XA_WM_NAME change, name is %s\n", __func__, c->name); update_titlebar = True; } } else if (e->atom == w->atoms[WM_TRANSIENT_FOR]) { Client *new_trans_client; Window trans_win = 0; int success = 0; dbg("%s() got a tranciency change\n", __func__); misc_trap_xerrors(); success = XGetTransientForHint(w->dpy, c->window, &trans_win); if (!misc_untrap_xerrors() && success) if ((new_trans_client = wm_find_client(w, trans_win, WINDOW)) != NULL) { Client *p = new_trans_client; dbg("%s() <%s> changing trans", __func__, new_trans_client->name); /* Dont let clients be change transiency to themselves * either directly or recursively. */ while (p != NULL) { if (p == c) { dbg("%s() CLIENT WARNING: %s ( %li ) transient for self\n", __func__, c->name, c->window); c->trans = NULL; return; } p = p->trans; } c->trans = new_trans_client; return; } c->trans = NULL; } else if (e->atom == w->atoms[_NET_WM_NAME]) { if (c->name) { XFree(c->name); c->name = NULL; } misc_trap_xerrors(); /* avoid possible X Errors */ c->name = (char*)ewmh_get_utf8_prop(w, c->window, w->atoms[_NET_WM_NAME]); misc_untrap_xerrors(); if (c->name) c->name_is_utf8 = True; else { c->name_is_utf8 = False; XFetchName(w->dpy, c->window, (char **)&c->name); } base_client_process_name(c); dbg("%s() NET_WM_NAME change, name is %s\n", __func__, c->name); update_titlebar = True; } else if (e->atom == w->atoms[WM_PROTOCOLS]) { int orig_flags = c->flags; dbg("%s() WM_PROTOCOLS changed, flags %i\n", __func__, orig_flags); client_get_wm_protocols(c); dbg("%s() WM_PROTOCOLS changed, flags now %li\n", __func__, c->flags); /* If flags have changed, likely means a WM_CONTEXT_HELP|etc has changed * and thus titlebar needs a repaint. */ if (c->flags != orig_flags) { dbg("%s() WM_PROTOCOLS changed flags, repainting\n", __func__); /* Buttons will get recreated on demand via repaint */ client_buttons_delete_all(c); update_titlebar = True; } } else if (e->atom == w->atoms[WM_CHANGE_STATE]) { dbg("%s() state change, name is %s\n", __func__, c->name); if(client_get_state(c) == WithdrawnState) { wm_remove_client(w, c); } } else if (e->atom == w->atoms[CM_TRANSLUCENCY]) { comp_engine_client_get_trans_prop(w, c); comp_engine_client_repair(w, c); } if (update_titlebar) c->redraw(c, False); } /* If configured force a app window be treated as a dialog */ Bool wm_win_force_dialog(Wm *w, Window win) { char *win_title = NULL; Bool result = False; if (!w->config->force_dialogs) return result; if (XFetchName(w->dpy, win, (char **)&win_title)) if (strstr(w->config->force_dialogs, win_title)) /* TODO: Improve search */ result = True; if (win_title) XFree(win_title); return result; } Client* wm_make_new_client(Wm *w, Window win) { Window trans_win; Atom realType, *value = NULL; unsigned long n, extra; int format, status; Client *c = NULL, *t = NULL; XWMHints *wmhints = NULL; int mwm_flags = 0; XGrabServer(w->dpy); dbg("%s() initiated\n", __func__); if (wm_win_force_dialog(w, win)) { /* Hackiness to allow app wins to be forced into dialogs * ( see -froce_dialogs switch ). * Much better to fix the app people! */ c = dialog_client_new(w, win, NULL); if (c == NULL) goto end; } else { /* Use EWMH Window Type Hint to figure out window type */ misc_trap_xerrors(); status = XGetWindowProperty(w->dpy, win, w->atoms[WINDOW_TYPE], 0L, 1000000L, 0, XA_ATOM, &realType, &format, &n, &extra, (unsigned char **) &value); if (misc_untrap_xerrors()) /* An X error occured - win deleted ? */ goto end; if (status == Success) { if (realType == XA_ATOM && format == 32) { dbg("%s() got type atom\n", __func__); if (value[0] == w->atoms[WINDOW_TYPE_DOCK]) { dbg("%s() got dock atom\n", __func__ ); c = dockbar_client_new(w, win); if (c == NULL) goto end; } else if (value[0] == w->atoms[WINDOW_TYPE_TOOLBAR] || value[0] == w->atoms[WINDOW_TYPE_INPUT]) { dbg("%s() got toolbar atom\n", __func__ ); c = toolbar_client_new(w, win); if (c == NULL) goto end; #ifdef USE_ALT_INPUT_WIN c->flags &= ~CLIENT_IS_MINIMIZED; #endif } else if (value[0] == w->atoms[WINDOW_TYPE_DESKTOP]) { dbg("%s() got desktop atom\n", __func__ ); c = desktop_client_new(w, win); if (c == NULL) goto end; } else if (value[0] == w->atoms[WINDOW_TYPE_SPLASH]) { dbg("%s() got splash atom\n", __func__ ); c = dialog_client_new(w, win, NULL); if (c == NULL) goto end; c->flags |= (CLIENT_TITLE_HIDDEN_FLAG|CLIENT_IS_SPLASH_WIN); } else if (value[0] == w->atoms[WINDOW_TYPE_DIALOG]) { dbg("%s() got type dialog atom\n", __func__ ); c = dialog_client_new(w, win, NULL); if (c == NULL) goto end; } else if (value[0] == w->atoms[WINDOW_TYPE_MENU] || value[0] == w->atoms[_NET_WM_WINDOW_TYPE_POPUP_MENU] || value[0] == w->atoms[_NET_WM_WINDOW_TYPE_DROPDOWN_MENU]) { dbg("%s() got type menu atom\n", __func__ ); c = dialog_client_new(w, win, NULL); if (c == NULL) goto end; c->flags |= CLIENT_IS_MENU_DIALOG; } else if (value[0] == w->atoms[WINDOW_TYPE_MESSAGE] /* Above to depricate for below */ || value[0] == w->atoms[_NET_WM_WINDOW_TYPE_NOTIFICATION]) { c = dialog_client_new(w, win, NULL); if (c == NULL) goto end; c->flags |= CLIENT_HAS_URGENCY_FLAG; } else if (value[0] == w->atoms[WINDOW_TYPE_NORMAL]) { c = main_client_new(w, win); if (c == NULL) goto end; c->flags ^= mwm_flags; } } } if (value) XFree(value); } if ((mwm_flags = mwm_get_decoration_flags(w, win))) { /* for now, treat just like a splash */ if (c == NULL) c = dialog_client_new(w, win, NULL); if (c) c->flags ^= mwm_flags; dbg("%s() got MWM flags: %li\n", __func__, c->flags ); } /* check for transient - ie detect if its a dialog */ XGetTransientForHint(w->dpy, win, &trans_win); if (trans_win && (trans_win != win)) { dbg("%s() Transient hint set\n", __func__); t = wm_find_client(w, trans_win, WINDOW); if (t == NULL) { /* Its transient for something, but not managed by us .. * We check for win groups in last act of desperation. */ Client *p = NULL; dbg("%s() transient window not managed\n", __func__); if ((wmhints = XGetWMHints(w->dpy, win)) != NULL) { if (wmhints->window_group && !stack_empty(w)) { stack_enumerate(w, p) if (wmhints->window_group == p->window) { t = p; break; } } if (wmhints) XFree(wmhints); } } dbg("%s() Transient ( %s) looks good, creating dialog\n", __func__, t ? t->name : "none" ); if (c == NULL) { /* if t is is NULL (from above), dialog will always be visible */ c = dialog_client_new(w, win, t); } else if (c->type == MBCLIENT_TYPE_DIALOG) /* already exists, update */ c->trans = t; /* TODO: what about other types being transient for things ?*/ /* Make sure above state is inherited if parent has it */ if (c->trans != NULL && c->trans->type == MBCLIENT_TYPE_DIALOG && c->trans->flags & CLIENT_HAS_ABOVE_STATE) c->flags |= CLIENT_HAS_ABOVE_STATE; } if (c == NULL) /* Noting else found, default to a main client */ { c = main_client_new(w, win); if (c == NULL) /* Something has gone wrong - prolly win dissapeared */ { dbg("%s() client dissapeared\n", __func__); goto end; } } /* We do this now as really needs to know window type */ base_client_process_name(c); dbg("%s() calling configure method for new client\n", __func__); c->configure(c); /* Size up the client */ comp_engine_client_init(w, c); dbg("%s() reparenting new client\n", __func__ ); c->reparent(c); /* reparent it to frames and decor */ dbg("%s() move/resizing new client\n", __func__); c->move_resize(c); /* set pos + size */ /* send new configuration to client - needed */ client_deliver_config(c); /* TODO: * * Its likely the size we given the new client, is not what it requested. * We've by now told the app its new size, but we need to give it a * chance to repaint itself at the new size or other wise we get horrible * flicker on mapping ( as remenants of old size are seen ). * * A possible solutions could be to implement the new _NET_WM_SYNC_REQUEST * stuff, as this is for resizes and we are resizing after all. * * XUngrabServer(w->dpy); * XSync(w->dpy, False); * XGrabServer(w->dpy); * * Note, this seems worst on GTK apps. */ XGrabButton(c->wm->dpy, Button1, 0, c->window, True, ButtonPressMask, GrabModeSync, GrabModeSync, None, None); /* Handle an application started iconized */ if (c->flags & CLIENT_IS_MINIMIZED && c->type == MBCLIENT_TYPE_APP) { /* Clear the flag now to be safe */ c->flags &= ~CLIENT_IS_MINIMIZED; c->redraw(c, False); /* draw the decorations ready */ c->iconize (c); ewmh_update_lists(w); XUngrabServer(w->dpy); XFlush(w->dpy); goto end; } dbg("%s() showing new client\n", __func__); c->redraw(c, False); /* draw the decorations ready */ wm_activate_client(c); /* Map it into stack, ( will call show()) */ /* Let window know were all done */ ewmh_state_set(c); /* XXX This is likely not needed */ client_set_state(c, NormalState); end: XUngrabServer(w->dpy); XFlush(w->dpy); return c; } void wm_remove_client(Wm *w, Client *c) { dbg("%s() called for %s\n", __func__, c->name); XGrabServer(w->dpy); misc_trap_xerrors(); /* below very likely to genrate X errors */ /* Set win state to withdrawn and reparent to root. * If below is not done - suttle bugs appear with toolkits ( GTK ) */ client_set_state(c, WithdrawnState); /* Below is likely not needed ( window will have already unmapped ) * and likely to fire an X error, but we do it anyway to be extra safe. * * In gtk quickly hiding/showing window repeatadly it can seem gtk does't * always call the unmap and a wierd ghost window is left. */ XUnmapWindow(w->dpy, c->window); /* Reparent now unmanaged win to root */ XReparentWindow(w->dpy, c->window, w->root, c->x, c->y); XRemoveFromSaveSet(w->dpy, c->window); /* sync here so any (likely) lingering X errors are trapped */ XSync(w->dpy, False); misc_untrap_xerrors(); c->destroy(c); /* If no clients left, force update of active props accordingly */ if (stack_empty(w) || w->stack_top_app == NULL) { ewmh_set_current_app_window(w); ewmh_set_active(w); } XUngrabServer(w->dpy); } /* wm_update_layout() is called in the presence of a panel/toolbar * changing its size / appearing. It re-layouts all windows for it * to fit. */ void wm_update_layout(Wm *w, Client *client_changed, signed int change_amount) /* XXX Change to relayout */ { Client *p = NULL; XGrabServer(w->dpy); stack_enumerate(w,p) { if (p == client_changed) continue; dbg("%s() restacking, comparing %i is less than %i for %s\n", __func__, p->y, client_changed->y, p->name); if (client_changed->type == MBCLIENT_TYPE_PANEL && client_changed->flags & CLIENT_DOCK_WEST) { if (p->x >= client_changed->x) { switch (p->type) { case MBCLIENT_TYPE_APP : if (p->flags & CLIENT_FULLSCREEN_FLAG) break; p->width += change_amount; p->x -= change_amount; p->move_resize(p); theme_img_cache_clear( w->mbtheme, FRAME_MAIN ); client_deliver_config(p); client_buttons_delete_all(p); theme_pixmap_cache_clear_all(w->mbtheme); main_client_redraw(p, False); /* force title redraw */ break; case MBCLIENT_TYPE_TOOLBAR : case MBCLIENT_TYPE_PANEL : if (p->flags & CLIENT_DOCK_EAST) break; if (p->flags & CLIENT_DOCK_TITLEBAR) { /* See notes below on this */ if (change_amount > 0) { XRectangle rect; mbtheme_get_titlebar_panel_rect(p->wm->mbtheme, &rect, client_changed); p->x = rect.x + wm_get_offsets_size(p->wm, WEST, client_changed, True); p->width = rect.width ; } else p->configure(p); } else { p->width += change_amount; p->x -= change_amount; } p->move_resize(p); client_deliver_config(p); default: break; } } } else if (client_changed->type == MBCLIENT_TYPE_PANEL && client_changed->flags & CLIENT_DOCK_EAST) { if (p->x <= client_changed->x) { switch (p->type) { case MBCLIENT_TYPE_APP : if (p->flags & CLIENT_FULLSCREEN_FLAG) break; p->width += change_amount; p->move_resize(p); client_deliver_config(p); theme_img_cache_clear( w->mbtheme, FRAME_MAIN ); theme_pixmap_cache_clear_all(w->mbtheme); client_buttons_delete_all(p); main_client_redraw(p, False); /* force title redraw */ break; case MBCLIENT_TYPE_TOOLBAR : case MBCLIENT_TYPE_PANEL : if (p->flags & CLIENT_DOCK_WEST) break; if (p->flags & CLIENT_DOCK_TITLEBAR) { /* The usual configure call takes into account the just removed dock ( +ive change amount ), so we have to basically do own configure() call ignoring this. */ if (change_amount > 0) { XRectangle rect; mbtheme_get_titlebar_panel_rect(p->wm->mbtheme, &rect, client_changed); p->x = rect.x + wm_get_offsets_size(p->wm, WEST, client_changed, True); p->width = rect.width ; } else p->configure(p); } else { p->width += change_amount; } p->move_resize(p); client_deliver_config(p); default: break; } } } else if (client_changed->type == MBCLIENT_TYPE_PANEL && client_changed->flags & CLIENT_DOCK_NORTH) { if (p->y >= client_changed->y) { switch (p->type) { case MBCLIENT_TYPE_APP : if (p->flags & CLIENT_FULLSCREEN_FLAG) break; p->height += change_amount; p->y -= change_amount; p->move_resize(p); /* XXX shouldn't be any need to redraw here as * width won't have changed so no need to repaint * entire toolbar. * * It *could* break on if the side decoration themes * are somehow a function of height. No themes I know * of do this and we avoid quite a lot of cpu avoiding * this. * * theme_img_cache_clear( w->mbtheme, FRAME_MAIN ); * theme_pixmap_cache_clear_all(w->mbtheme); * main_client_redraw(p, False); */ client_deliver_config(p); break; case MBCLIENT_TYPE_PANEL : if (p->flags & CLIENT_DOCK_NORTH || p->flags & CLIENT_DOCK_TITLEBAR) { p->y -= change_amount; p->move_resize(p); client_deliver_config(p); } break; default: break; } } } else { /* toolbar changes here */ dbg("%s(): restack NORMAL comparing %i <= %i for %s\n", __func__, p->y, client_changed->y, p->name); if ( (p->y <= client_changed->y) || (client_changed->type == MBCLIENT_TYPE_PANEL && p->type == MBCLIENT_TYPE_TOOLBAR)) { dbg("%s() restacking ( NORMAL )%s", __func__, p->name); switch (p->type) { case MBCLIENT_TYPE_APP : if (p->flags & CLIENT_FULLSCREEN_FLAG && client_changed->type == MBCLIENT_TYPE_PANEL) break; p->height += change_amount; p->move_resize(p); /* * See above as to why this is commented out. * * theme_img_cache_clear( w->mbtheme, FRAME_MAIN ); * theme_pixmap_cache_clear_all(w->mbtheme); * main_client_redraw(p, False); */ client_deliver_config(p); break; case MBCLIENT_TYPE_TOOLBAR : p->y += change_amount; p->move_resize(p); client_deliver_config(p); break; case MBCLIENT_TYPE_PANEL : if (p->flags & CLIENT_DOCK_SOUTH) { p->y += change_amount; p->move_resize(p); client_deliver_config(p); } break; case MBCLIENT_TYPE_DIALOG : default: break; } } } } /* Handle dialog centering etc */ stack_enumerate(w, p) { if (p->type == MBCLIENT_TYPE_DIALOG) { Bool force_height = False; int req_x = p->x, req_y = p->y, req_w = p->width, req_h = p->height; #ifdef USE_ALT_INPUT_WIN /* Alternate Input windows use the dialog type to * fit in with stacking etc. below is a bit of a hack * to avoid getting repositioned like a dialog. */ if (p->flags & (CLIENT_TB_ALT_TRANS_FOR_DIALOG |CLIENT_TB_ALT_TRANS_FOR_APP)) { p->configure(p); p->move_resize(p); client_deliver_config(p); continue; } #else /* Always try and back to intially requested height, a * panel or input win may have dissapeared giving more * space. * NOTE: we avoid doing this for ALT_INPUT_WINS (maemo) */ if (p->init_height && p->init_height > req_h) { req_h = p->init_height; force_height = True; } #endif if (!dialog_constrain_geometry(p, &req_x, &req_y, &req_w, &req_h) || force_height) { p->x = req_x; p->y = req_y; p->width = req_w; p->height = req_h; p->move_resize(p); client_deliver_config(p); } } } ewmh_update_rects(w); XSync(w->dpy, False); XUngrabServer(w->dpy); } static void wm_stack_dialogs_for_transient(Wm *w, Client *trans) { /* Correctly stack dialogs in respect to a transient ( app/desktop ) * parent. Utility call for activate_client() */ if (trans == NULL) return; /* Move transient dialogs to top */ stack_move_transients_to_top(w, trans, 0); /* Above dialogs go above transient for root */ stack_move_transients_to_top(w, trans, CLIENT_HAS_ABOVE_STATE /* CLIENT_HAS_URGENCY_FLAG */); /* Move transient for root dialogs to very top */ stack_move_transients_to_top(w, NULL, 0); /* Move transient for root with state above to top */ stack_move_transients_to_top(w, NULL, CLIENT_HAS_ABOVE_STATE ); /* Now move transient for root messages to top */ stack_move_transients_to_top(w, NULL, CLIENT_HAS_URGENCY_FLAG); } /* wm_activate_client() is called to 'activate', eg raise or show * a client stack wise. */ void wm_activate_client(Client *c) { Wm *w; Client *client_to_focus = c; Bool set_desktop_show_hint = False, set_current_app_active_hint = False; #ifndef NO_PING Client *prev_app_client = NULL; #endif if (c == NULL) return; /* its possible for this to happen :( */ w = c->wm; dbg("%s() called for %s\n", __func__, c->name); #ifndef NO_PING if (w->config->ping_aggressive) { prev_app_client = wm_get_visible_main_client(w); } #endif XGrabServer(w->dpy); c->show(c); /* Set 'relative' pos in stack, map windows if needed etc */ dbg("%s() DESKTOP_RAISED_FLAG is %i\n", __func__, (w->flags & DESKTOP_RAISED_FLAG)); if (c->type == MBCLIENT_TYPE_APP || c->type == MBCLIENT_TYPE_DESKTOP) { /* As matchbox works around 'main' windows ( apps/main and desktop wins). We need to sync extra stuff up when displaying a new one. */ Bool switching_from_to_fullscreen = False; /* save focus state for transient dialogs of prev showing main win */ if (w->stack_top_app && w->stack_top_app != c) { if ( (w->stack_top_app->flags & CLIENT_FULLSCREEN_FLAG && !(c->flags & CLIENT_FULLSCREEN_FLAG)) || ( !(w->stack_top_app->flags & CLIENT_FULLSCREEN_FLAG) && c->flags & CLIENT_FULLSCREEN_FLAG ) ) switching_from_to_fullscreen = True; w->stack_top_app->hide(w->stack_top_app); } /* If this client has a saved dialog focus state, load it */ if (c->next_focused_client && c->next_focused_client->type == MBCLIENT_TYPE_DIALOG) client_to_focus = c->next_focused_client; /* If transient for root dialog currently has focus then keep it that way. */ if (w->focused_client && w->focused_client->type == MBCLIENT_TYPE_DIALOG) { Client *lowest_dialog = w->focused_client; /* Get lowest transient dialog, and check its trans for root */ while (lowest_dialog->trans != NULL && lowest_dialog->type == MBCLIENT_TYPE_DIALOG) lowest_dialog = lowest_dialog->trans; if (lowest_dialog->trans == NULL && lowest_dialog->type == MBCLIENT_TYPE_DIALOG) { dbg("%s() keeping focus to root dialog\n", __func__); client_to_focus = w->focused_client; } } /* Raise panel + toolbars just above app but below app dialogs */ if (!(c->flags & CLIENT_FULLSCREEN_FLAG)) { dbg("%s() moving panels above %s\n", __func__, c->name); stack_move_type_above_client(w, MBCLIENT_TYPE_PANEL, c); stack_dump(w); } stack_move_type_above_client(w, MBCLIENT_TYPE_TOOLBAR, c); dbg("%s() c is trans for <%s>\n", __func__, c->trans ? c->trans->name : "nothing"); wm_stack_dialogs_for_transient(w, c); /* Deal with desktop flag etc */ if (c->type != MBCLIENT_TYPE_DESKTOP && !(c->flags & CLIENT_IS_DESKTOP_FLAG) ) /* ^^^^^^^^^ hack for decorated desktop */ { dbg("%s() clearing desktop flag\n", __func__); w->flags &= ~DESKTOP_RAISED_FLAG; w->stack_top_app = c; set_desktop_show_hint = True; } else { /* Desktop being activated */ dbg("%s() setting desktop raised flag\n", __func__); w->flags |= DESKTOP_RAISED_FLAG; /* Make sure embedded titlebar panels arn't visible for desktop */ if (w->have_titlebar_panel && mbtheme_has_titlebar_panel(w->mbtheme) && !(w->have_titlebar_panel->flags & CLIENT_DOCK_TITLEBAR_SHOW_ON_DESKTOP)) { stack_move_above_client(w->have_titlebar_panel, NULL); } set_desktop_show_hint = True; } /* Set active main client, set focus handles active win hint */ set_current_app_active_hint = True; #ifdef USE_ALT_INPUT_WIN /* If switching from/to fullscreen, then there could be an * input window transient for a transient for root dialog and * it therefore needs to be resized ( the IM ). */ if (switching_from_to_fullscreen && c->type == MBCLIENT_TYPE_APP) main_client_manage_toolbars_for_fullscreen(c, False); #endif } else if (c->type == MBCLIENT_TYPE_DIALOG) { /* A Little insurance - on mapping, a dialog can end up below * panels and toolbars. May be a cleaner way than this. */ Client *top_main_client = NULL; if (!(w->flags & DESKTOP_RAISED_FLAG)) { if ((top_main_client = wm_get_visible_main_client(w)) != NULL) { stack_dump(w); dbg("%s() now raising panels+toolbar above dialog\n", __func__); if (!(top_main_client->flags & CLIENT_FULLSCREEN_FLAG)) /* Move just above main app win, therefore *below* dialogs */ stack_move_type_above_client(w, MBCLIENT_TYPE_TOOLBAR |MBCLIENT_TYPE_PANEL, top_main_client); } else /* move type above nothing - eg to bottom */ stack_move_type_above_client(w, MBCLIENT_TYPE_TOOLBAR |MBCLIENT_TYPE_PANEL, NULL); } else { /* Desktop is raised. dialogs ( trans for desktop ) can end up * below panels for some reason on initial map so we take * below sledgehammer approach which seems to fix things. */ wm_stack_dialogs_for_transient(w, wm_get_visible_main_client(w)); } /* Check if this is transient for an app/desktop thats * not currently at the top of the stack. In which case * dont try and focus it. */ if (c->trans != NULL && wm_get_visible_main_client(w)) { Client *dialog_parent = c->trans; top_main_client = wm_get_visible_main_client(w); while (dialog_parent->trans != NULL) dialog_parent = dialog_parent->trans; if ( (dialog_parent->type == MBCLIENT_TYPE_APP || dialog_parent->type == MBCLIENT_TYPE_DESKTOP) && top_main_client != dialog_parent && top_main_client->win_group != c->win_group) client_to_focus = NULL; } } /* Always make sure embedded titlebar panels arn't visible for desktop */ if (c == w->have_titlebar_panel && w->flags & DESKTOP_RAISED_FLAG && mbtheme_has_titlebar_panel(w->mbtheme) && !(w->have_titlebar_panel->flags & CLIENT_DOCK_TITLEBAR_SHOW_ON_DESKTOP)) { /* TODO: Change to stack move bottom ? * * eg use stack_move_above_client(c, NULL) */ stack_move_above_client(c, NULL); } ewmh_update_lists(w); if (set_desktop_show_hint) ewmh_update_desktop_hint(w); dbg("%s() now syncing above window stack\n", __func__); stack_sync_to_display(w); if (c->flags & CLIENT_DELAY_MAPPING) /* See dialog show() source */ { c->flags &= ~CLIENT_DELAY_MAPPING; XMapSubwindows(w->dpy, c->frame); XMapWindow(w->dpy, c->frame); } client_set_focus(client_to_focus); /* set focus if needed and ewmh active */ stack_dump(w); if (set_current_app_active_hint) ewmh_set_current_app_window(w); comp_engine_client_show(w, c); #ifndef NO_PING if (w->config->ping_aggressive) { Client *new_app_client; new_app_client = wm_get_visible_main_client(w); if (prev_app_client != new_app_client) { /* Only stop pings here now, activates via NET_ACTIVE * * if (new_app_client * && new_app_client->type != MBCLIENT_TYPE_DESKTOP) * { * ewmh_ping_client_start (new_app_client); * } */ if (prev_app_client) { ewmh_ping_client_stop (prev_app_client); } } } #endif XSync(w->dpy, False); XUngrabServer(w->dpy); } /* Returns either desktop or main app client */ Client* wm_get_visible_main_client(Wm *w) { if (w->flags & DESKTOP_RAISED_FLAG) { dbg("%s() returning desktop - %p\n", __func__, wm_get_desktop(w)); return wm_get_desktop(w); } if (w->stack_top_app) { dbg("%s() returning stack top : %p\n", __func__, w->stack_top_app); return w->stack_top_app; } dbg("%s() returning NULL\n", __func__); return NULL; } /* Get area taken up on an edge by panels, toolbars */ int wm_get_offsets_size(Wm* w, int wanted_direction, Client* ignore_client, Bool include_toolbars ) { Client *p; int x, y, width, height, result = 0; if (stack_empty(w)) return 0; dbg("%s() called\n", __func__); stack_enumerate(w, p) { if ((ignore_client && p == ignore_client) || p->mapped == False) continue; switch(wanted_direction) { case NORTH: if (p->type == MBCLIENT_TYPE_PANEL && p->flags & CLIENT_DOCK_NORTH) { p->get_coverage(p, &x, &y, &width, &height); result += height; } break; case SOUTH: if ((p->type == MBCLIENT_TYPE_PANEL && p->flags & CLIENT_DOCK_SOUTH) || (p->type == MBCLIENT_TYPE_TOOLBAR && include_toolbars) ) { p->get_coverage(p, &x, &y, &width, &height); result += height; } break; case EAST: if (p->type == MBCLIENT_TYPE_PANEL && p->flags & CLIENT_DOCK_EAST) { p->get_coverage(p, &x, &y, &width, &height); result += width; } break; case WEST: if (p->type == MBCLIENT_TYPE_PANEL && p->flags & CLIENT_DOCK_WEST) { p->get_coverage(p, &x, &y, &width, &height); result += width; } break; } } return result; } void wm_toggle_desktop(Wm *w) { dbg("%s() called desktop flag is : %i \n", __func__, (w->flags & DESKTOP_RAISED_FLAG)); if (!wm_get_desktop(w)) { dbg("%s() couldn't find desktop \n", __func__ ); return; } if (w->flags & DESKTOP_RAISED_FLAG) { dbg("%s() hiding desktop\n", __func__); wm_activate_client(w->stack_top_app); } else { dbg("%s() showing desktop\n", __func__); wm_activate_client(wm_get_desktop(w)); } } void wm_set_cursor_visibility(Wm *w, Bool visible) { #ifdef HAVE_XFIXES int major = 0, minor = 0, ev_base, err_base; if (XFixesQueryExtension (w->dpy, &ev_base, &err_base)) XFixesQueryVersion (w->dpy, &major, &minor); dbg("Checking Xfixes, got %i.%i , ev (%i), err (%i)", major, minor, ev_base, err_base); if (major >= 4) { w->curs = XCreateFontCursor(w->dpy, XC_left_ptr); XDefineCursor(w->dpy, w->root, w->curs); if (visible) { w->config->no_cursor = False; misc_trap_xerrors(); /* Appears to fire X error if not already hidden * but no way to query that ? */ XFixesShowCursor (w->dpy, w->root); XSync(w->dpy, False); misc_untrap_xerrors(); } else { XFixesHideCursor (w->dpy, w->root); } } else #endif { /* FIXME: do we need to free the cursors ? */ if (visible) { w->config->no_cursor = False; w->curs = XCreateFontCursor(w->dpy, XC_left_ptr); } else { Pixmap pix = XCreatePixmap (w->dpy, w->root, 1, 1, 1); XColor col; memset (&col, 0, sizeof (col)); w->blank_curs = XCreatePixmapCursor (w->dpy, pix, pix, &col, &col, 1, 1); w->curs = w->blank_curs; XFreePixmap (w->dpy, pix); w->config->no_cursor = True; } XDefineCursor(w->dpy, w->root, w->curs); } } Client * wm_get_desktop(Wm *w) { return w->client_desktop; } #ifdef USE_XSETTINGS #define XSET_UNKNOWN 0 #define XSET_THEME 1 #define XSET_CURSOR 2 #define XSET_LOWLIGHT 3 #define XSET_TITLEBARS 4 #define XSET_COMPOSITE 5 #define XSET_CURSOR_THEME_NAME 6 #define XSET_CURSOR_THEME_SIZE 7 static void wm_xsettings_notify_cb (const char *name, XSettingsAction action, XSettingsSetting *setting, void *data) { Wm *w = (Wm *)data; int i = 0; int key = XSET_UNKNOWN; struct _mb_xsettings { char *name; int value; } mb_xsettings[] = { { "MATCHBOX/THEME", XSET_THEME }, { "MATCHBOX/CURSOR", XSET_CURSOR }, { "MATCHBOX/TITLEBARS", XSET_TITLEBARS }, /* XXX Not implemeted */ { "MATCHBOX/COMPOSITE", XSET_COMPOSITE }, { "Gtk/CursorThemeName", XSET_CURSOR_THEME_NAME }, { "Gtk/CursorThemeSize", XSET_CURSOR_THEME_SIZE }, { NULL, -1 } }; while( mb_xsettings[i].name != NULL ) { if (!strcmp(name, mb_xsettings[i].name) && setting != NULL) /* Set to NULL when action deleted */ { key = mb_xsettings[i].value; break; } i++; } if (key == XSET_UNKNOWN) return; if (setting->type == XSETTINGS_TYPE_STRING) { switch (action) { case XSETTINGS_ACTION_NEW: case XSETTINGS_ACTION_CHANGED: switch (key) { case XSET_COMPOSITE: if (!strcasecmp("off", setting->data.v_string) || !strcasecmp("false", setting->data.v_string)) { comp_engine_deinit(w); } else { comp_engine_reinit(w); } break; case XSET_THEME: if (w->flags & STARTUP_FLAG) w->config->theme = strdup(setting->data.v_string); else mbtheme_switch(w, setting->data.v_string); break; case XSET_CURSOR: if (!strcasecmp("true", setting->data.v_string)) wm_set_cursor_visibility(w, True); else wm_set_cursor_visibility(w, False); break; case XSET_TITLEBARS: /* XXX todo */ break; case XSET_CURSOR_THEME_NAME: #ifdef HAVE_XCURSOR XcursorSetTheme (w->dpy, setting->data.v_string); #endif break; default: break; } case XSETTINGS_ACTION_DELETED: /* Do nothing for now */ break; } } else if (setting->type == XSETTINGS_TYPE_INT) { #ifdef HAVE_XCURSOR if ( (action == XSETTINGS_ACTION_NEW || action == XSETTINGS_ACTION_CHANGED) && key == XSET_CURSOR_THEME_NAME) { if (setting->data.v_int) XcursorSetDefaultSize (w->dpy, setting->data.v_int); } #endif } } #endif #ifdef USE_LIBSN /* Various stuff for startup notification libs. * Mb uses it for both busy startup cursor and single instancing apps. * Seems alot of code for just that .... */ static void wm_sn_exec(Wm *w, char* name, char* bin_name, char *desc) { SnLauncherContext *context = NULL; pid_t child_pid = 0; context = sn_launcher_context_new (w->sn_display, DefaultScreen (w->dpy)); if (name) sn_launcher_context_set_name (context, name); if (desc) sn_launcher_context_set_description (context, desc); if (bin_name) sn_launcher_context_set_binary_name (context, bin_name); sn_launcher_context_initiate (context, "Matchbox-kb-shortcut", bin_name, CurrentTime); switch ((child_pid = fork ())) { case -1: fprintf (stderr, "Fork failed\n" ); break; case 0: sn_launcher_context_setup_child_process (context); execlp(bin_name, bin_name, (char *)NULL); fprintf (stderr, "Failed to exec %s \n", bin_name); _exit (1); break; } sn_launcher_context_unref (context); } static void wm_sn_timeout_check (Wm *w) { time_t now ; dbg("%s() called\n", __func__); if (!w->sn_busy_cnt) return; now = time(NULL); if ((now - w->sn_init_time) > MB_SN_APP_TIMEOUT) { w->sn_busy_cnt--; w->sn_init_time = time(NULL); } if (w->sn_busy_cnt) XDefineCursor(w->dpy, w->root, w->curs_busy); else { XDefineCursor(w->dpy, w->root, w->curs); XDeleteProperty(w->dpy, w->root, w->atoms[MB_CLIENT_STARTUP_LIST]); } } static void wm_sn_cycle_update_root_prop(Wm *w) { SnCycle *current_cycle = w->sn_cycles; char *prop_str = NULL; int prop_str_len = 0; ewmh_update_lists(w); if (current_cycle == NULL) { XDeleteProperty(w->dpy, w->root, w->atoms[MB_CLIENT_STARTUP_LIST]); XFlush(w->dpy); return; } XGrabServer(w->dpy); while(current_cycle != NULL) { dbg("%s() looping on %s, %li\n", __func__, current_cycle->bin_name, current_cycle->xid ); if (current_cycle->xid == None) { dbg("%s() adding %s, %li\n", __func__, current_cycle->bin_name, current_cycle->xid ); prop_str_len += (strlen(current_cycle->bin_name) + 1); } current_cycle = current_cycle->next; } if (prop_str_len > 1) { prop_str = malloc(sizeof(char)*(prop_str_len+1)); memset(prop_str, 0, prop_str_len+1); current_cycle = w->sn_cycles; while(current_cycle != NULL) { if (current_cycle->xid == None) { strcat(prop_str, current_cycle->bin_name); strcat(prop_str, "|"); } current_cycle = current_cycle->next; } dbg("%s() Setting MB_CLIENT_STARTUP_LIST to %s\n", __func__, prop_str); if (prop_str) { XChangeProperty(w->dpy, w->root, w->atoms[MB_CLIENT_STARTUP_LIST] , XA_STRING, 8, PropModeReplace, (unsigned char *)prop_str, strlen(prop_str) ); free(prop_str); } } else { dbg("%s() Deleting MB_CLIENT_STARTUP_LIST \n", __func__ ); XDeleteProperty(w->dpy, w->root, w->atoms[MB_CLIENT_STARTUP_LIST]); } XFlush(w->dpy); XUngrabServer(w->dpy); } static SnCycle * wm_sn_cycle_new(Wm *w, const char *bin_name) { SnCycle *new_cycle = malloc(sizeof(SnCycle)); memset(new_cycle, 0, sizeof(SnCycle)); new_cycle->bin_name = strdup(bin_name); new_cycle->xid = None; new_cycle->next = NULL; return new_cycle; } static void wm_sn_cycle_add(Wm *w, const char *bin_name) { SnCycle *current_cycle; dbg("%s() called with %s\n", __func__, bin_name); if (w->sn_cycles == NULL) { w->sn_cycles = wm_sn_cycle_new(w, bin_name); } else { current_cycle = w->sn_cycles; if (!strcmp(current_cycle->bin_name, bin_name) && current_cycle->xid == None) { dbg("%s() already have %s\n", __func__, bin_name); return; /* already have it */ } while (current_cycle->next != NULL) { if (!strcmp(current_cycle->bin_name, bin_name) && current_cycle->xid == None ) { dbg("%s() already have %s\n", __func__, bin_name); return; /* already have it */ } current_cycle = current_cycle->next; } current_cycle->next = wm_sn_cycle_new(w, bin_name); } wm_sn_cycle_update_root_prop(w); } void wm_sn_cycle_remove(Wm *w, Window xid) { SnCycle *current_cycle = w->sn_cycles, *prev_cycle = NULL; while(current_cycle != NULL) { if (current_cycle->xid == xid) { if (current_cycle == w->sn_cycles) { w->sn_cycles = current_cycle->next; dbg("%s(): removed, w->sn_cycles is now %p\n", __func__, w->sn_cycles); } else { prev_cycle->next = current_cycle->next; } free(current_cycle->bin_name); free(current_cycle); wm_sn_cycle_update_root_prop(w); return; } prev_cycle = current_cycle; current_cycle = current_cycle->next; } wm_sn_cycle_update_root_prop(w); } static void wm_sn_cycle_update_xid(Wm *w, const char *bin_name, Window xid) { /* find first where xid is None, and update */ /* in above check, check another dont exist _without_ xid */ /* destroy must call sn_cycle remove ? - unless theme switch flag is on */ SnCycle *current_cycle = w->sn_cycles; dbg("%s() called with %s, %li\n", __func__, bin_name, xid); while(current_cycle != NULL) { if (!strcmp(current_cycle->bin_name, bin_name) && current_cycle->xid == None) { dbg("%s() got match for %s, setting xid = %li\n", __func__, bin_name, xid); current_cycle->xid = xid; wm_sn_cycle_update_root_prop(w); return; } current_cycle = current_cycle->next; } dbg("%s() match failed\n", __func__); wm_sn_cycle_update_root_prop(w); } static void wm_sn_monitor_event_func (SnMonitorEvent *event, void *user_data) { SnStartupSequence *sequence; Wm *w = (Wm *)user_data; const char *seq_id = NULL, *bin_name = NULL; Client *p; dbg("%s() called\n", __func__); sequence = sn_monitor_event_get_startup_sequence (event); if (sequence == NULL) { dbg("%s() failed, context / sequence is NULL\n", __func__); return; } seq_id = sn_startup_sequence_get_id (sequence); bin_name = sn_startup_sequence_get_binary_name (sequence); if (seq_id == NULL || bin_name == NULL) { dbg("%s() failed, seq_id or bin_name NULL \n", __func__ ); return; } switch (sn_monitor_event_get_type (event)) { case SN_MONITOR_EVENT_INITIATED: dbg("%s() SN_MONITOR_EVENT_INITIATED\n", __func__); w->sn_busy_cnt++; w->sn_init_time = time(NULL); wm_sn_cycle_add(w, bin_name); break; case SN_MONITOR_EVENT_CHANGED: dbg("%s() SN_MONITOR_EVENT_CHANGED\n", __func__); break; case SN_MONITOR_EVENT_COMPLETED: dbg("%s() SN_MONITOR_EVENT_COMPLETED\n", __func__ ); if (!stack_empty(w)) { stack_enumerate(w, p) { if (p->startup_id && !strcmp(p->startup_id, seq_id)) { dbg("%s() found startup_id match ( %s ) for %s \n", __func__, seq_id, p->name ); wm_sn_cycle_update_xid(w, bin_name, p->window); wm_sn_cycle_update_root_prop(w); w->sn_busy_cnt--; break; } } } else w->sn_busy_cnt--; break; case SN_MONITOR_EVENT_CANCELED: /* wm_sn_cycle_remove(w, bin_name); */ w->sn_busy_cnt--; break; } if (w->sn_busy_cnt) XDefineCursor(w->dpy, w->root, w->curs_busy); else XDefineCursor(w->dpy, w->root, w->curs); } #endif /* Hacky way of dimming windows when no composite - not recommended */ #ifndef USE_COMPOSITE void wm_lowlight(Wm *w, Client *c) { #ifndef STANDALONE MBPixbufImage *img; int x, y; Pixmap pxm_tmp; XSetWindowAttributes attr; attr.override_redirect = True; attr.event_mask = ChildMask|ButtonPressMask|ExposureMask; c->frame = XCreateWindow(w->dpy, w->root, 0, 0, w->dpy_width, w->dpy_height, 0, CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect|CWEventMask, &attr); pxm_tmp = XCreatePixmap(c->wm->dpy, c->window, w->dpy_width, w->dpy_height , w->pb->depth); img = mb_pixbuf_img_new_from_x_drawable(c->wm->pb, w->root, None, 0, 0, w->dpy_width, w->dpy_height, True); XMapWindow(w->dpy, c->frame); for (x = 0; x < w->dpy_width; x++) for (y = 0; y < w->dpy_height; y++) mb_pixbuf_img_plot_pixel_with_alpha(c->wm->pb, img, x, y, w->config->lowlight_params[0], w->config->lowlight_params[1], w->config->lowlight_params[2], w->config->lowlight_params[3] ); /* Striped pattern diabled. if ( (y % 6) > 2 ) { mb_pixbuf_img_composite_pixel(img, x, y, 0, 0, 0, 150); } else { mb_pixbuf_img_composite_pixel(img, x, y, 0, 0, 0, 100); } */ mb_pixbuf_img_render_to_drawable(w->pb, img, pxm_tmp, 0, 0); XSetWindowBackgroundPixmap(w->dpy, c->frame, pxm_tmp); XClearWindow(w->dpy, c->frame); mb_pixbuf_img_free(w->pb, img); XFreePixmap(w->dpy, pxm_tmp); #endif } #endif #ifdef USE_GCONF void gconf_key_changed_callback (GConfClient *client, guint cnxn_id, GConfEntry *entry, gpointer user_data) { Wm *w = (Wm *)user_data; GConfValue *value = NULL; char *key = NULL; dbg("%s() called\n", __func__ ); value = gconf_entry_get_value(entry); key = (char *)gconf_entry_get_key(entry); if (value && key) { dbg("%s() key is %s\n", __func__, key ); switch (value->type) { case GCONF_VALUE_STRING: dbg("%s() value is string : %s\n", __func__, gconf_value_get_string(value) ); /* On a keychange, we just reload the whole config :/ */ if (strstr(key, "keybindings")) { dbg("%s() calling keys_reinit\n", __func__ ); keys_reinit(w); } else if (!strcmp(key, "/apps/matchbox/general/theme")) { #ifndef USE_XSETTINGS char *theme = gconf_client_get_string(w->gconf_client, "/apps/matchbox/general/theme", NULL); if (w->flags & STARTUP_FLAG) w->config->theme = strdup(theme); else mbtheme_switch(w, theme); #else if (w->xsettings_client == NULL) { char *theme = gconf_client_get_string(w->gconf_client, "/apps/matchbox/general/theme", NULL); if (w->flags & STARTUP_FLAG) w->config->theme = strdup(theme); else mbtheme_switch(w, theme); } #endif } break; case GCONF_VALUE_BOOL: dbg("%s() value is boolean : %s\n", __func__, (gconf_value_get_bool(value)) ? "True" : "False" ); break; case GCONF_VALUE_INT: dbg("%s() value is int : %i\n", __func__, gconf_value_get_int(value)); break; default : dbg("%s() value is useless to me...\n", __func__ ); } } } #endif