/* -*-c-*- */ /* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * This module is all original code * by Rob Nation * Copyright 1993, Robert Nation * You may use this code for any purpose, as long as the original * copyright remains in the source code and all documentation */ /* * * code for talking with fvwm modules. * */ #include "config.h" #include #include #include #include "libs/ftime.h" #include "libs/fvwmlib.h" #include "libs/FScreen.h" #include "libs/ColorUtils.h" #include "libs/Parse.h" #include "libs/Strings.h" #include "libs/wild.h" #include "fvwm.h" #include "externs.h" #include "functions.h" #include "bindings.h" #include "misc.h" #include "screen.h" #include "module_interface.h" #include "module_list.h" #include "events.h" #include "geometry.h" #include "libs/fvwmsignal.h" #include "decorations.h" #include "commands.h" /* A queue of commands from the modules */ static fqueue cqueue = FQUEUE_INIT; static const unsigned long dummy = 0; static unsigned long * make_vpacket(unsigned long *body, unsigned long event_type, unsigned long num, va_list ap) { unsigned long *bp = body; /* truncate long packets */ if (num > FvwmPacketMaxSize) { num = FvwmPacketMaxSize; } *(bp++) = START_FLAG; *(bp++) = event_type; *(bp++) = num+FvwmPacketHeaderSize; *(bp++) = fev_get_evtime(); for (; num > 0; --num) { *(bp++) = va_arg(ap, unsigned long); } return body; } /* RBW - 04/16/1999 - new packet builder for GSFR -- Arguments are pairs of lengths and argument data pointers. RBW - 05/01/2000 - A length of zero means that an int is being passed which must be stored in the packet as an unsigned long. This is a special hack to accommodate the old CONFIGARGS technique of sending the args for the M_CONFIGURE_WINDOW packet. */ static unsigned long make_new_vpacket(unsigned char *body, unsigned long event_type, unsigned long num, va_list ap) { long arglen; unsigned long addlen; unsigned long bodylen = 0; unsigned long *bp = (unsigned long *)body; unsigned long *bp1 = bp; unsigned long plen = 0; *(bp++) = START_FLAG; *(bp++) = event_type; /* Skip length field, we don't know it yet. */ bp++; *(bp++) = fev_get_evtime(); for (; num > 0; --num) { arglen = va_arg(ap, long); if (arglen <= 0) { if (arglen == 0) { arglen = -sizeof(int); } addlen = sizeof(unsigned long); } else { addlen = arglen; } bodylen += addlen; if (bodylen >= FvwmPacketMaxSize_byte) { fvwm_msg( ERR, "make_new_vpacket", "packet too long %ld %ld", (long)bodylen, (long)FvwmPacketMaxSize_byte); break; } if (arglen > 0) { register char *tmp = (char *)bp; memcpy(tmp, va_arg(ap, char *), arglen); tmp += arglen; bp = (unsigned long *)tmp; } else if (arglen == 0 || arglen == -sizeof(int)) { int *tmp; tmp = va_arg(ap, int *); *bp = (unsigned long) *tmp; bp++; } else if (arglen == -sizeof(long)) { unsigned long *tmp; tmp = va_arg(ap, unsigned long *); *bp = (unsigned long) *tmp; bp++; } else if (arglen == -sizeof(short)) { short *tmp; tmp = va_arg(ap, short *); *bp = (unsigned long) *tmp; bp++; } else { fvwm_msg( ERR, "make_new_vpacket", "can not handle arglen %ld, please contact" " fvwm-workers@fvwm.org. aborting...", arglen); abort(); } } /* Round up to a long word boundary. Most of the module interface still thinks in terms of an array of longss, so let's humor it. */ plen = (unsigned long) ((char *)bp - (char *)bp1); plen = ((plen + (sizeof(long) - 1)) / sizeof(long)) * sizeof(long); *(((unsigned long*)bp1)+2) = (plen / (sizeof(unsigned long))); return plen; } void SendPacket( fmodule *module, unsigned long event_type, unsigned long num_datum, ...) { unsigned long body[FvwmPacketMaxSize]; va_list ap; va_start(ap, num_datum); make_vpacket(body, event_type, num_datum, ap); va_end(ap); PositiveWrite( module, body, (num_datum+FvwmPacketHeaderSize)*sizeof(body[0])); return; } void BroadcastPacket(unsigned long event_type, unsigned long num_datum, ...) { unsigned long body[FvwmPacketMaxSize]; va_list ap; fmodule_list_itr moditr; fmodule *module; va_start(ap,num_datum); make_vpacket(body, event_type, num_datum, ap); va_end(ap); module_list_itr_init(&moditr); while ( (module = module_list_itr_next(&moditr)) != NULL) { PositiveWrite( module, body, (num_datum+FvwmPacketHeaderSize)*sizeof(body[0])); } return; } /* RBW - 04/16/1999 - new style packet senders for GSFR -- */ static void SendNewPacket( fmodule *module, unsigned long event_type, unsigned long num_datum, ...) { unsigned char body[FvwmPacketMaxSize_byte]; va_list ap; unsigned long plen; va_start(ap,num_datum); plen = make_new_vpacket(body, event_type, num_datum, ap); va_end(ap); PositiveWrite(module, (void *) &body, plen); return; } static void BroadcastNewPacket(unsigned long event_type, unsigned long num_datum, ...) { unsigned char body[FvwmPacketMaxSize_byte]; va_list ap; fmodule_list_itr moditr; fmodule *module; unsigned long plen; va_start(ap,num_datum); plen = make_new_vpacket(body, event_type, num_datum, ap); va_end(ap); module_list_itr_init(&moditr); while ( (module = module_list_itr_next(&moditr)) != NULL) { PositiveWrite(module, (void *) &body, plen); } return; } action_flags *__get_allowed_actions(const FvwmWindow *fw) { static action_flags act; act.is_movable = is_function_allowed( F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False); act.is_deletable = is_function_allowed( F_DELETE, NULL, fw, RQORIG_PROGRAM_US, False); act.is_destroyable = is_function_allowed( F_DESTROY, NULL, fw, RQORIG_PROGRAM_US, False); act.is_closable = is_function_allowed( F_CLOSE, NULL, fw, RQORIG_PROGRAM_US, False); act.is_maximizable = is_function_allowed( F_MAXIMIZE, NULL, fw, RQORIG_PROGRAM_US, False); act.is_resizable = is_function_allowed( F_RESIZE, NULL, fw, RQORIG_PROGRAM_US, False); act.is_iconifiable = is_function_allowed( F_ICONIFY, NULL, fw, RQORIG_PROGRAM_US, False); return &act; } /* RBW - 04/16/1999 - new version for GSFR -- - args are now pairs: - length of arg data - pointer to arg data - number of arguments is the number of length/pointer pairs. - the 9th field, where flags used to be, is temporarily left as a dummy to preserve alignment of the other fields in the old packet: we should drop this before the next release. */ #define CONFIGARGS(_fw) 33, \ (unsigned long)(-sizeof(Window)), \ &FW_W(*(_fw)), \ (unsigned long)(-sizeof(Window)), \ &FW_W_FRAME(*(_fw)), \ (unsigned long)(-sizeof(void *)), \ &(_fw), \ (unsigned long)(0), \ &(*(_fw))->g.frame.x, \ (unsigned long)(0), \ &(*(_fw))->g.frame.y, \ (unsigned long)(0), \ &(*(_fw))->g.frame.width, \ (unsigned long)(0), \ &(*(_fw))->g.frame.height, \ (unsigned long)(0), \ &(*(_fw))->Desk, \ (unsigned long)(0), \ &(*(_fw))->layer, \ (unsigned long)(0), \ &(*(_fw))->hints.base_width, \ (unsigned long)(0), \ &(*(_fw))->hints.base_height, \ (unsigned long)(0), \ &(*(_fw))->hints.width_inc, \ (unsigned long)(0), \ &(*(_fw))->hints.height_inc, \ (unsigned long)(0), \ &(*(_fw))->orig_hints.width_inc, \ (unsigned long)(0), \ &(*(_fw))->orig_hints.height_inc, \ (unsigned long)(0), \ &(*(_fw))->hints.min_width, \ (unsigned long)(0), \ &(*(_fw))->hints.min_height, \ (unsigned long)(0), \ &(*(_fw))->hints.max_width, \ (unsigned long)(0), \ &(*(_fw))->hints.max_height, \ (unsigned long)(-sizeof(Window)), \ &FW_W_ICON_TITLE(*(_fw)), \ (unsigned long)(-sizeof(Window)), \ &FW_W_ICON_PIXMAP(*(_fw)), \ (unsigned long)(0), \ &(*(_fw))->hints.win_gravity, \ (unsigned long)(-sizeof(Pixel)), \ &(*(_fw))->colors.fore, \ (unsigned long)(-sizeof(Pixel)), \ &(*(_fw))->colors.back, \ (unsigned long)(0), \ &(*(_fw))->ewmh_hint_layer, \ (unsigned long)(sizeof(unsigned long)), \ &(*(_fw))->ewmh_hint_desktop, \ (unsigned long)(0), \ &(*(_fw))->ewmh_window_type, \ (unsigned long)(sizeof(short)), \ &(*(_fw))->title_thickness, \ (unsigned long)(sizeof(short)), \ &(*(_fw))->boundary_width, \ (unsigned long)(sizeof(short)), \ &dummy, \ (unsigned long)(sizeof(short)), \ &dummy, \ (unsigned long)(sizeof((*(_fw))->flags)), \ &(*(_fw))->flags, \ (unsigned long)(sizeof(action_flags)), \ __get_allowed_actions((*(_fw))) void SendConfig(fmodule *module, unsigned long event_type, const FvwmWindow *t) { const FvwmWindow **t1 = &t; /* RBW- SendPacket(module, event_type, CONFIGARGS(t)); */ SendNewPacket(module, event_type, CONFIGARGS(t1)); return; } void BroadcastConfig(unsigned long event_type, const FvwmWindow *t) { const FvwmWindow **t1 = &t; /* RBW- BroadcastPacket(event_type, CONFIGARGS(t)); */ BroadcastNewPacket(event_type, CONFIGARGS(t1)); return; } static unsigned long *make_named_packet( int *len, unsigned long event_type, const char *name, int num, ...) { unsigned long *body; va_list ap; /* Packet is the header plus the items plus enough items to hold the * name string. */ *len = FvwmPacketHeaderSize + num + (strlen(name) / sizeof(unsigned long)) + 1; /* truncate long packets */ if (*len > FvwmPacketMaxSize) { *len = FvwmPacketMaxSize; } body = (unsigned long *)safemalloc(*len * sizeof(unsigned long)); /* Zero out end of memory to avoid uninit memory access. */ body[*len-1] = 0; va_start(ap, num); make_vpacket(body, event_type, num, ap); va_end(ap); strncpy((char *)&body[FvwmPacketHeaderSize+num], name, (*len - FvwmPacketHeaderSize - num)*sizeof(unsigned long) - 1); body[2] = *len; return (body); } void SendName( fmodule *module, unsigned long event_type, unsigned long data1,unsigned long data2, unsigned long data3, const char *name) { unsigned long *body; int l; if (name == NULL) { return; } body = make_named_packet(&l, event_type, name, 3, data1, data2, data3); PositiveWrite(module, body, l*sizeof(unsigned long)); free(body); return; } void BroadcastName( unsigned long event_type, unsigned long data1, unsigned long data2, unsigned long data3, const char *name) { unsigned long *body; int l; fmodule_list_itr moditr; fmodule *module; if (name == NULL) { return; } body = make_named_packet(&l, event_type, name, 3, data1, data2, data3); module_list_itr_init(&moditr); while ( (module = module_list_itr_next(&moditr)) != NULL) { PositiveWrite(module, body, l*sizeof(unsigned long)); } free(body); return; } void BroadcastWindowIconNames(FvwmWindow *fw, Bool window, Bool icon) { if (window) { BroadcastName( M_WINDOW_NAME, FW_W(fw), FW_W_FRAME(fw), (unsigned long)fw, fw->name.name); BroadcastName( M_VISIBLE_NAME, FW_W(fw), FW_W_FRAME(fw), (unsigned long)fw, fw->visible_name); } if (icon) { BroadcastName( M_ICON_NAME, FW_W(fw), FW_W_FRAME(fw), (unsigned long)fw, fw->icon_name.name); BroadcastName( MX_VISIBLE_ICON_NAME, FW_W(fw), FW_W_FRAME(fw), (unsigned long)fw, fw->visible_icon_name); } return; } void SendFvwmPicture( fmodule *module, unsigned long event_type, unsigned long data1, unsigned long data2, unsigned long data3, FvwmPicture *picture, char *name) { unsigned long *body; unsigned long data4 = 0, data5 = 0, data6 = 0, data7 = 0, data8 = 0, data9 = 0; int l; if (!FMiniIconsSupported) { return; } if ((name == NULL) || (event_type != M_MINI_ICON)) { return; } if (picture != NULL) { data4 = picture->width; data5 = picture->height; data6 = picture->depth; data7 = picture->picture; data8 = picture->mask; data9 = picture->alpha; } body = make_named_packet( &l, event_type, name, 9, data1, data2, data3, data4, data5, data6, data7, data8, data9); PositiveWrite(module, body, l*sizeof(unsigned long)); free(body); return; } void BroadcastFvwmPicture( unsigned long event_type, unsigned long data1, unsigned long data2, unsigned long data3, FvwmPicture *picture, char *name) { unsigned long *body; unsigned long data4, data5, data6, data7, data8, data9; int l; fmodule_list_itr moditr; fmodule *module; if (!FMiniIconsSupported) { return; } if (picture != NULL) { data4 = picture->width; data5 = picture->height; data6 = picture->depth; data7 = picture->picture; data8 = picture->mask; data9 = picture->alpha; } else { data4 = 0; data5 = 0; data6 = 0; data7 = 0; data8 = 0; data9 = 0; } body = make_named_packet( &l, event_type, name, 9, data1, data2, data3, data4, data5, data6, data7, data8, data9); module_list_itr_init(&moditr); while ( (module = module_list_itr_next(&moditr)) != NULL) { PositiveWrite(module, body, l*sizeof(unsigned long)); } free(body); return; } /* * Reads a colorset command from a module and broadcasts it back out */ void BroadcastColorset(int n) { fmodule_list_itr moditr; fmodule *module; char *buf; buf = DumpColorset(n, &Colorset[n]); module_list_itr_init(&moditr); while ( (module = module_list_itr_next(&moditr)) != NULL) { SendName(module, M_CONFIG_INFO, 0, 0, 0, buf); } return; } /* * Broadcasts a string to all modules as M_CONFIG_INFO. */ void BroadcastPropertyChange( unsigned long argument, unsigned long data1, unsigned long data2, char *string) { fmodule_list_itr moditr; fmodule *module; module_list_itr_init(&moditr); while ( (module = module_list_itr_next(&moditr)) != NULL) { SendName(module, MX_PROPERTY_CHANGE, argument, data1, data2, string); } return; } /* * Broadcasts a string to all modules as M_CONFIG_INFO. */ void BroadcastConfigInfoString(char *string) { fmodule_list_itr moditr; fmodule *module; module_list_itr_init(&moditr); while ( (module = module_list_itr_next(&moditr)) != NULL) { SendName(module, M_CONFIG_INFO, 0, 0, 0, string); } return; } /* * Broadcasts the state of Xinerama support to all modules as M_CONFIG_INFO. */ void broadcast_xinerama_state(void) { BroadcastConfigInfoString((char *)FScreenGetConfiguration()); return; } /* * Broadcasts the ignored modifiers to all modules as M_CONFIG_INFO. */ void broadcast_ignore_modifiers(void) { char msg[32]; sprintf(msg, "IgnoreModifiers %d", GetUnusedModifiers()); BroadcastConfigInfoString(msg); return; } /* run the input command as if it cames from a button press or release */ void module_input_execute(struct fmodule_input *input) { XEvent e; const exec_context_t *exc; exec_context_changes_t ecc; int flags; memset(&e, 0, sizeof(e)); if (XFindContext(dpy, input->window, FvwmContext, (caddr_t *)&ecc.w.fw) == XCNOENT) { ecc.w.fw = NULL; input->window = None; } /* Query the pointer, the pager-drag-out feature doesn't work properly. * This is OK now that the Pager uses "Move pointer" * A real fix would be for the modules to pass the button press coords */ if (FQueryPointer( dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX,&JunkY, &e.xbutton.x_root, &e.xbutton.y_root, &e.xbutton.state) == False) { /* pointer is not on this screen */ /* If a module does XUngrabPointer(), it can now get proper * Popups */ e.xbutton.window = Scr.Root; ecc.w.fw = NULL; } else { e.xbutton.window = input->window; } e.xbutton.subwindow = None; e.xbutton.button = 1; /* If a module does XUngrabPointer(), it can now get proper Popups */ if (StrEquals(input->command, "popup")) { e.xbutton.type = ButtonPress; e.xbutton.state |= Button1Mask; } else { e.xbutton.type = ButtonRelease; e.xbutton.state &= (~(Button1Mask)); } e.xbutton.x = 0; e.xbutton.y = 0; fev_fake_event(&e); ecc.type = EXCT_MODULE; ecc.w.w = input->window; flags = (input->window == None) ? 0 : FUNC_DONT_DEFER; ecc.w.wcontext = GetContext(NULL, ecc.w.fw, &e, &(input->window)); ecc.x.etrigger = &e; ecc.m.module = input->module; exc = exc_create_context( &ecc, ECC_TYPE | ECC_ETRIGGER | ECC_FW | ECC_W | ECC_WCONTEXT | ECC_MODULE); execute_function(NULL, exc, input->command, 0); exc_destroy_context(exc); module_input_discard(input); return; } /* enqueue a module command on the command queue to be executed later */ void module_input_enqueue(struct fmodule_input *input) { if (input == NULL) { return; } DBUG("module_input_enqueue", input->command); fqueue_add_at_end(&cqueue, (void*)input); } /* * * Procedure: * ExecuteCommandQueue - runs command from the module command queue * This may be called recursively if a module command runs a function * that does a Wait, so it must be re-entrant * */ void ExecuteCommandQueue(void) { fmodule_input *input; while (fqueue_get_first(&cqueue, (void **)&input) == 1) { /* remove from queue */ fqueue_remove_or_operate_from_front( &cqueue, NULL, NULL, NULL, NULL); /* execute and destroy */ if (input->command) { DBUG("ExecuteCommandQueue", input->command); module_input_execute(input); } else { module_input_discard(input); } } return; } /* ** send an arbitrary string to all instances of a module */ void CMD_SendToModule(F_CMD_ARGS) { char *name,*str; unsigned long data0, data1, data2; fmodule_list_itr moditr; fmodule *module; FvwmWindow * const fw = exc->w.fw; /* FIXME: Without this, popup menus can't be implemented properly in * modules. Olivier: Why ? */ /* UngrabEm(); */ if (!action) { return; } str = GetNextToken(action, &name); if (!name) { return; } if (fw) { /* Modules may need to know which window this applies to */ data0 = FW_W(fw); data1 = FW_W_FRAME(fw); data2 = (unsigned long)fw; } else { data0 = 0; data1 = 0; data2 = 0; } module_list_itr_init(&moditr); while ( (module = module_list_itr_next(&moditr)) != NULL) { if ( (MOD_NAME(module) != NULL && matchWildcards(name,MOD_NAME(module))) || (MOD_ALIAS(module) && matchWildcards(name, MOD_ALIAS(module)))) { SendName(module,M_STRING,data0,data1,data2,str); FlushMessageQueue(module); } } free(name); return; } /* ** send an arbitrary string back to the calling module */ void CMD_Send_Reply(F_CMD_ARGS) { unsigned long data0, data1, data2; fmodule *module = exc->m.module; FvwmWindow * const fw = exc->w.fw; if (module == NULL) { return; } if (!action) { return; } if (fw) { /* Modules may need to know which window this applies to */ data0 = FW_W(fw); data1 = FW_W_FRAME(fw); data2 = (unsigned long)fw; } else { data0 = 0; data1 = 0; data2 = 0; } SendName(module, MX_REPLY, data0, data1, data2, action); FlushMessageQueue(module); return; } void CMD_Send_WindowList(F_CMD_ARGS) { FvwmWindow *t; fmodule *mod = exc->m.module; if (mod == NULL) { return; } SendPacket(mod, M_NEW_DESK, 1, (long)Scr.CurrentDesk); SendPacket( mod, M_NEW_PAGE, 7, (long)Scr.Vx, (long)Scr.Vy, (long)Scr.CurrentDesk, (long)Scr.MyDisplayWidth, (long)Scr.MyDisplayHeight, (long)((Scr.VxMax / Scr.MyDisplayWidth) + 1), (long)((Scr.VyMax / Scr.MyDisplayHeight) + 1)); if (Scr.Hilite != NULL) { SendPacket( mod, M_FOCUS_CHANGE, 5, (long)FW_W(Scr.Hilite), (long)FW_W_FRAME(Scr.Hilite), (unsigned long)True, (long)Scr.Hilite->hicolors.fore, (long)Scr.Hilite->hicolors.back); } else { SendPacket( mod, M_FOCUS_CHANGE, 5, 0, 0, (unsigned long)True, (long)GetColor(DEFAULT_FORE_COLOR), (long)GetColor(DEFAULT_BACK_COLOR)); } if (Scr.DefaultIcon != NULL) { SendName(mod, M_DEFAULTICON, 0, 0, 0, Scr.DefaultIcon); } for (t = Scr.FvwmRoot.next; t != NULL; t = t->next) { SendConfig(mod,M_CONFIGURE_WINDOW,t); SendName( mod, M_WINDOW_NAME, FW_W(t), FW_W_FRAME(t), (unsigned long)t, t->name.name); SendName( mod, M_ICON_NAME, FW_W(t), FW_W_FRAME(t), (unsigned long)t, t->icon_name.name); SendName( mod, M_VISIBLE_NAME, FW_W(t), FW_W_FRAME(t), (unsigned long)t, t->visible_name); SendName( mod, MX_VISIBLE_ICON_NAME, FW_W(t), FW_W_FRAME(t), (unsigned long)t,t->visible_icon_name); if (t->icon_bitmap_file != NULL && t->icon_bitmap_file != Scr.DefaultIcon) { SendName( mod, M_ICON_FILE, FW_W(t), FW_W_FRAME(t), (unsigned long)t, t->icon_bitmap_file); } SendName( mod, M_RES_CLASS, FW_W(t), FW_W_FRAME(t), (unsigned long)t, t->class.res_class); SendName( mod, M_RES_NAME, FW_W(t), FW_W_FRAME(t), (unsigned long)t, t->class.res_name); if (IS_ICONIFIED(t) && !IS_ICON_UNMAPPED(t)) { rectangle r; Bool rc; rc = get_visible_icon_geometry(t, &r); if (rc == True) { SendPacket( mod, M_ICONIFY, 7, (long)FW_W(t), (long)FW_W_FRAME(t), (unsigned long)t, (long)r.x, (long)r.y, (long)r.width, (long)r.height); } } if ((IS_ICONIFIED(t))&&(IS_ICON_UNMAPPED(t))) { SendPacket( mod, M_ICONIFY, 7, (long)FW_W(t), (long)FW_W_FRAME(t), (unsigned long)t, (long)0, (long)0, (long)0, (long)0); } if (FMiniIconsSupported && t->mini_icon != NULL) { SendFvwmPicture( mod, M_MINI_ICON, FW_W(t), FW_W_FRAME(t), (unsigned long)t, t->mini_icon, t->mini_pixmap_file); } } if (Scr.Hilite == NULL) { BroadcastPacket( M_FOCUS_CHANGE, 5, (long)0, (long)0, (unsigned long)True, (long)GetColor(DEFAULT_FORE_COLOR), (long)GetColor(DEFAULT_BACK_COLOR)); } else { BroadcastPacket( M_FOCUS_CHANGE, 5, (long)FW_W(Scr.Hilite), (long)FW_W(Scr.Hilite), (unsigned long)True, (long)Scr.Hilite->hicolors.fore, (long)Scr.Hilite->hicolors.back); } SendPacket(mod, M_END_WINDOWLIST, 0); return; }