diff options
Diffstat (limited to 'gui/gtk/gtkeyboard.c')
-rw-r--r-- | gui/gtk/gtkeyboard.c | 2353 |
1 files changed, 2353 insertions, 0 deletions
diff --git a/gui/gtk/gtkeyboard.c b/gui/gtk/gtkeyboard.c new file mode 100644 index 00000000..1963c839 --- /dev/null +++ b/gui/gtk/gtkeyboard.c @@ -0,0 +1,2353 @@ +/* app.c + * For use with GTKeyboard + * written by David Allen, s2mdalle@titan.vcu.edu + * http://opop.nols.com/ + * + * #define DEBUGGING at compile time for interesting info most people don't + * want to see. + */ +/* GTKeyboard - A Graphical Keyboard For X + * Copyright (C) 1999, 2000 David Allen + * + * 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 Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <glib.h> +#include <gtk/gtk.h> +#include <gdk/gdk.h> +#include <gdk/gdkx.h> +#include <signal.h> +#include <dirent.h> +#include <sys/stat.h> +#include <unistd.h> +#include <ctype.h> +#include <assert.h> +#include <time.h> +#include <sys/time.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> +#include <X11/Xos.h> +#include <X11/extensions/shape.h> +#include <X11/Xmu/WinUtil.h> +#include <X11/cursorfont.h> +#include <X11/keysym.h> +#include <stdarg.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/types.h> + +/* The regular non-fun glib calls for distro copies of gtkeyboard. */ +# define g_new0_(t,c) g_new0(t,c) +# define g_free_(mem) g_free(mem) +# define g_new_(t,c) g_new(t,c) +# define g_strdup_(x) g_strdup(x) +# define g_malloc_(x) g_malloc(x) +# define MEM(x) ; /* Don't do anything */ + + +/* modmap.h + * Written by David Allen <s2mdalle@titan.vcu.edu> + * + * Released under the terms of the GNU General Public License + */ + + + + +#define slot_number_to_mask(x) (1<<x) + +typedef struct { + KeyCode codes[4]; +} ModmapRow; + +typedef struct { + int max_keypermod; /* Alias for the entry in XModifierMap */ + ModmapRow modifiers[8]; /* Exactly 8 entries */ +} ModmapTable; + +static int file_exists(const char *filename); +static void send_redirect_a_keysym(KeySym input); +static unsigned long find_modifier_mask(KeyCode code); +static ModmapTable *ModmapTable_new(void); +static void ModmapTable_destroy(ModmapTable * table); +static int ModmapTable_insert(ModmapTable * table, KeyCode code, int slot); +static int mask_name_to_slot_number(char *maskname); + + +/* templates.h + * + * Written by David Allen <s2mdalle@titan.vcu.edu> + * http://opop.nols.com/ + * Released under the terms of the GNU General Public License + */ + +#define MAXIMUM_ROWS 6 + +/* Abstract data types */ + +typedef struct { + KeySym lower_case; /* What happens when pressed by itself */ + KeySym upper_case; /* What happens when pressed with Shift/CL */ + KeySym alt_gr; /* Alt GR+ button -- mostly unused */ + KeyCode code; + char *aux_string; /* Cheating string holder for things that aren't + * right with XKeysymToString() -- most notably + * the F keys and the keypad keys + * This should NEVER be used in conjunction + * with foreign windows - only text insertion in + * our editing buffer + */ +} KEY; + +typedef struct { + char *tab; + char *backspace; + char *caps_lock; + char *space; + char *alt; + char *alt_gr; + char *control; + char *shift; +} KeyboardTranslation; + +typedef struct { + int row_values[MAXIMUM_ROWS]; + int keycount; + KeySym *syms; + KeyCode *codes; + KeyboardTranslation *trans; + char *name; + ModmapTable *modmap; +} KEYBOARD; + +/* Macros */ +#define NO_KEYBOARD ((KEYBOARD *)NULL) +#define NO_KEY ((KEY *)NULL) + +/* Function prototypes */ +static KEY *gtkeyboard_key_copy(KEY * key); +static KEY *gtkeyboard_keyboard_get_key(KEYBOARD * keyb, int row, int keyno); +static KEYBOARD *gtkeyboard_destroy_keyboard(KEYBOARD * input); +static KEYBOARD *read_keyboard_template(char *filename); +static KEY *gtkeyboard_destroy_key(KEY * input); +static KEY *gtkeyboard_new_key(const KeySym lower, + const KeySym upper, + const KeySym altgr, const char *alt); + + + +/* rc_file.h - A simple configuration file reader/writer header file + * by Patrick Gallot <patrick.gallot@cimlinc.com> + * + * This file is part of GTKeyboard and as such is licensed under the terms + * of the GNU General Public License. + */ + + +/* Different types that resource file varaibles can have */ +#define RC_NONE 0x0000 +#define RC_STR 0x0002 /* String */ +#define RC_PARSE_FUNC 0x0006 /* This one means that an external + * parsing function should be used + * and the resource file code + * shouldn't bother with the values + */ +typedef struct config_var { + /* These go together to specify a line in a file that could say something + * like "set foo_variable 10" or "foobar = 20" or even + * "toolbar menubar off" + */ + + gchar *Prefix; /* This could be something like "set" or "toolbar" */ + gchar *Name; /* Element name */ + gint Type; /* Type of the value */ + gpointer Val; /* Pointer to the value to store */ + /* Function pointer for custom handling -- it should have parameters + * of the "prefix" token, (usually "set"), the variable name, the + * varaible value, and the pointer we were supposed to store it in. + * The pointer is usually going to be the same as Val above + */ + int (*func) (char *prefix, char *varname, char *val, gpointer ptr); +} RCVARS; + +static int read_ConfigFile(char *filename, RCVARS * vars, int complain); + +#define CONDFREE(x) if(x){ g_free_(x); x = NULL; } + +#define FLUSH_EVERYTHING fflush(Q); fflush(stdout);\ + fflush(stderr); + +/* Toggling Defines */ +#define ON 1 +#define OFF 0 +/* Whatever the status is, FLIP IT */ +#define ISOFF(x) ( x==OFF ) + +/* In case we ever achieve portability to winblows, we can change this + * to a backslash. :) + */ +#define _DIR "/" + +#define FLUSH fflush(Q) +#define NONE NULL + +#define LEFT_MOUSE_BUTTON 1 +#define RIGHT_MOUSE_BUTTON 3 +#define MIDDLE_MOUSE_BUTTON 2 + +#define Q stderr +#define ABOUT_INFO "about.data" +#define RC_FILENAME ".gtkeyboardrc" +#define PROVIDED_RCFILE "example_configurations/defaults.gtkeyboard" +#define APPNAME "GTKeyboard" +#define CR "\n" + + + +/* adt.h + * For GTKeyboard + * written by David Allen s2mdalle@titan.vcu.edu + * http://opop.nols.com/ + * + * This file is released under the terms of the GNU General Public License. + * Please see COPYING for more details. + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <glib.h> +#include <gtk/gtk.h> +#include <errno.h> +#include <signal.h> +#include <ctype.h> +#include <assert.h> +#include <time.h> +#include <sys/time.h> + +/* Maximum number of custom colors. 10 is a good number, above that is + * really not needed per se. + */ +#define MAX_CUSTOM_COLORS 10 + +/**************************************************************************/ + +/* ADTs */ +/* Program configuration settings */ +typedef struct { + /* Changed to bits in 0.97.3 to save a little space. */ + /* Different program options - initialized in app.c changed in + * file_manip.c and messed with elsewhere. + * + * See app.c and the initialization code for comments on what they + * all mean. + */ + int REDIRECT_POLICY_IMPLICIT; + int STATUS_LOGGING; + int CAPS_LOCK; + int IGNORE_LAYOUT_FILE; + /* int ASK_REMAP_ON_EXIT; -- Deprecated for now... */ + int SHIFT; + int ALT_GR; + int NUMLOCK; + int VERBOSE; + int ALT; + int CONTROL; + int USE_KEYBOARD; + + + char *home; /* Home dir of user running the app. */ + char *tempslot; + char *extrafiles; /* Extra files, docs, default rcfile, lic. */ + char *redirect_window_name; /* Window name of redirection location. */ + char *keyboard_file; + char *cache_file; + Window redirect_window; /* Xlib window structure for redirection. */ + Window other; /* Temporary window structure for Xlib */ + + KEYBOARD *keyboard; + KEYBOARD *old_keyboard; + + /* These memory areas won't be used unless PROD isn't defined. + * When PROD isn't defined, we're not debugging, and they essentially + * keep track of the number of memory calls that I make. See also + * mem_header.h for a full definition of how they're used. + */ +#if defined(GTKEYBOARD_MEMORY_DEBUGGING) || defined(DEBUGGING) + long MEM; /* Number of bytes allocated */ + int gnew; /* Number of gnew() calls (alloc) */ + int gnew0; /* Number of gnew0() calls (alloc) */ + int gmalloc; /* Number of g_malloc() calls (alloc) */ + int gstrdup; /* Number of g_strdup() calls (alloc) */ + int gfree; /* Number of g_free() calls (free) */ +#endif +} GTKeyboardOptions; + +typedef struct { + GtkWidget *keyboard; + gint show_keyboard; +} GTKeyboardKeyboardElements; + +typedef struct { + int keyboard; +} GTKeyboardElements; + +/****************************************************************************/ + +typedef struct { + Window xwindow; /* So we can play with xlib. (our window) */ + GtkWidget *KEYBOARD; /* Container containing all keyb elements */ + + + GTKeyboardKeyboardElements keyboard_elements; + + /* If a field has a comment of "Cleaned" next to it, then it is + * either deallocated or destroyed in + * callbacks.c:gtkeyboard_mem_cleanup() via macro calls. + */ + char *custom_colors[MAX_CUSTOM_COLORS]; /* Cleaned */ + gchar *fontname; /* Cleaned */ + gchar *kfontname; /* Cleaned */ + gchar *colorname; /* Cleaned */ + gdouble saved_colors[4]; + + GtkShadowType SHADOW_TYPE; + GtkPositionType PULLOFFS_SIDE; + + GtkItemFactory *item_factory; /* Cleaned */ + GtkWidget *popup_menu; /* Cleaned */ + GdkCursor *cursor; + GtkStyle *style; + + /* Style referring to the main output text area */ + GtkStyle *textstyle; /* Cleaned */ + + /* The default GTK style */ + GtkStyle *deflt; /* Cleaned */ + + GdkFont *font; /* Cleaned */ + + /* The keyboard widget's style */ + GtkStyle *kstyle; /* Cleaned */ + + /* Keyboard font */ + GdkFont *kfont; /* Cleaned */ + + GtkTooltips *tooltips; +} GTKeyboardGUI; + +/**************************************************************************/ + +typedef struct { + int width; + int height; + int x; + int y; +} window_dimensions; + + +/* file_manip.h + * + * For use with GTKeyboard + * written by David Allen s2mdalle@titan.vcu.edu + * This file is released under the terms of the GNU General Public License + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <gtk/gtk.h> + + +#define MAX_LINE_LENGTH 128 +#define TOKEN_SIZE 50 +#define DEFAULT_RCFILE "# Default .gtkeyboardrc file written by GTKeyboard\nkeyboard main_keyboard on\nkeyboard number_pad on\nkeyboard cursor_keys on\nkeyboard f_keys on\n\nset handlebars off\n\nset word_wrap off\nset info_popups on\nset bump_amount 15\nhide buttonbar\nset random_strlen 10\nset eyecandy off\nset redirect_policy implicit\n" + + + + +#define STAT_AND_RETURN_IF_BAD(fname, someint, statstruct) \ + if(!fname)return(0);\ + someint = stat(fname, &statstruct);\ + if(someint<0) return(0); + +/* List of strings - GTKeyboard is really looking for these when it + * reads the rcfiles. These are the options it recognizes. To the user, + * they aren't case sensitive, but here, they must ALL be specified in all caps + */ +#define OPT_KEYBOARD_FILE "KEYBOARD_FILE" + + +/* Prototypes - creeping evil */ +static void parse_user_resource_file(char *filename); +static void FILE_readline(FILE * fp, char *buffer, const int maxlen); +static int setup_default_rcfile(char *file); + +/* EOF */ + +static const char *active_window_name = "CurrentWindow"; + +static Window root; +static XEvent xev; + +/* fake_timestamp is static in irxevent.c */ +static Window find_window(Window top, char *name); +static void gtkeyboard_XEvent_common_setup(XKeyEvent * xev); + +/* Various keyboards are included only for app.c and build_keyboard.c + * in master.h + */ + +/* Just make them all different - gets passed to build_an_entire_keyboard */ +/* Not used yet.. */ + +/* Local defines for what keys appear as */ + +/* + * Check the notes at the bottom of the page for an ASCII drawing of how the + * keyboard should look + * + * Each button is defined as a character string of two characters. The first + * is what you get when SHIFT || CAPS_LOCK > 0 and the second is the normal + * key function. This function loops through grabbing two characters at a + * time and then calling makekeybutton to set things up and map the + * callback to the key and so on. + * + * SPECIAL CASES WITHIN stuff[]: (Yes it's braindamaged) + * LL is the left cursor key + * RR is the right cursor key + * || is caps lock key + * \\ (or escaped, it's \\\\) is SHIFT key + * CC is control + * AA is alt + * +*/ + +/* PROTOTYPES - Most functions in build_keyboard.c are static and don't need + * prototypes here. + */ +static void keysym_callback(GtkWidget * emitter, gpointer data); +static void capslock_toggle(GtkWidget * w, gpointer data); +static void alt_toggle(GtkWidget * w, gpointer data); +static void alt_gr_toggle(GtkWidget * w, gpointer data); +static void control_toggle(GtkWidget * w, gpointer data); +static void shift_on(GtkWidget * w, gpointer data); + +typedef struct { + GtkStyle *style; + int mask; +} GTKeyboardStyle; + +typedef struct { + GtkWidget *dialog; + GdkColor *color; +} color_info; + +typedef struct { + long code; + char *name; +} binding; + + +/* Main program options - do not declare as static - all global */ +static GTKeyboardOptions options; +static GTKeyboardGUI GUI; +static GTKeyboardElements ELEMENTS; + + +#define NO_HANDLER 666 + +/* Static function prototypes */ +static void setup_default_opts(void); +static void setup_default_opts_other(void); + +#define INSTALL_DOC_DIR "/usr/local/share/gtkeyboard" + +int init_keyboard_stuff(char *input) +{ + char *filename; + char *homeptr; + + /* This sets the values in the structure GUI to default values. + * I'll add things for changing the default in that function later. + */ + setup_default_opts(); /* Handles the stuff in options */ + setup_default_opts_other(); + + /* Allocate the necessary memory to copy location of support files + * into */ + options.extrafiles = g_new0_(char, (strlen(INSTALL_DOC_DIR) + 3)); + sprintf(options.extrafiles, "%s%s", INSTALL_DOC_DIR, _DIR); + + options.keyboard = NO_KEYBOARD; + options.old_keyboard = NO_KEYBOARD; + + /* End if */ + homeptr = getenv("HOME"); + + if (homeptr) { + options.home = g_strdup_(getenv("HOME")); + } /* End if */ + else + options.home = NULL; + + filename = + g_new0_(char, strlen(options.home) + 5 + strlen(RC_FILENAME)); + sprintf(filename, "%s%s%s", (options.home ? options.home : "/tmp"), + _DIR, RC_FILENAME); + + /* This parses ALL user preferences from ~/.gtkeyboardrc into various + * structures - the important parts of this are in rc_file.h and + * file_manip.c + */ + + parse_user_resource_file(filename); + + CONDFREE(filename); + + return (1); +} /* End init_keyboard_stuff */ + +/* This gets called once at setup to initialize all the GUI but + * non-keyboard related opts + * Shouldn't return anything or allocate any memory, it just sets + * things to "reasonable" defaults. + */ +static void setup_default_opts_other(void) +{ + /* Here's where you get to set all of the defaults for GUI structure + * members. + */ + GUI.SHADOW_TYPE = GTK_SHADOW_ETCHED_OUT; + GUI.PULLOFFS_SIDE = GTK_POS_LEFT; + GUI.cursor = (GdkCursor *) NULL; + GUI.fontname = (gchar *) NULL; + GUI.kfontname = (gchar *) NULL; + GUI.colorname = (gchar *) NULL; + GUI.textstyle = (GtkStyle *) NULL; + GUI.style = (GtkStyle *) NULL; + GUI.font = (GdkFont *) NULL; + GUI.kfont = (GdkFont *) NULL; + GUI.deflt = (GtkStyle *) NULL; + GUI.xwindow = (Window) NULL; + GUI.popup_menu = (GtkWidget *) NULL; + GUI.item_factory = (GtkItemFactory *) NULL; + + GUI.keyboard_elements.show_keyboard = ON; + + GUI.keyboard_elements.keyboard = (GtkWidget *) NULL; + + return; +} /* End setup_default_opts_other */ + +/* This gets called once at startup, sets all the initial values for + * options.whatever stuff + */ +static void setup_default_opts(void) +{ + /* All screen ELEMENTS on by default */ + + ELEMENTS.keyboard = 1; + + /* Set up some default values for program parameters + * Can be changed by parse_rcfile later. Hopefully these are reasonable. + */ + options.other = (Window) NULL; + options.redirect_window = (Window) NULL; + options.home = (char *) NULL; + options.redirect_window_name = (char *) NULL; + options.tempslot = (char *) NULL; + options.keyboard_file = (char *) NULL; + + + /* options.ASK_REMAP_ON_EXIT = OFF; *//* Currently deprecated */ + options.STATUS_LOGGING = OFF; /* Save the status window */ + options.CAPS_LOCK = OFF; /* Caps lock starts at OFF */ + options.SHIFT = OFF; /* Shift starts at OFF */ + options.ALT_GR = OFF; /* Alt-GR key status */ + options.CONTROL = OFF; /* Control button at OFF */ + options.NUMLOCK = OFF; /* Numlock starts at OFF */ + options.ALT = OFF; /* Alt key starts at OFF */ + options.VERBOSE = OFF; /* Spew messages */ + options.REDIRECT_POLICY_IMPLICIT = ON; /* Default to implicit redir */ + options.IGNORE_LAYOUT_FILE = OFF; /* Dont ignore layout file */ + +} /* End setup_default_opts */ + + + +#define BUILD_KEYBOARD_C + +static char *string_special_cases(KeySym input); + +static void rotate_keyboard_definitions(KEYBOARD * new_keyboard) +{ + if (!new_keyboard) { + fprintf(stderr, + "rotate_keyboard_definitions Error: Bad keyboard.\n"); + fflush(stderr); + return; + } + + /* End if */ + /* If there is an old definition laying around, destroy it to free + * up the memory. + */ + if (options.old_keyboard) { + options.old_keyboard = + gtkeyboard_destroy_keyboard(options.old_keyboard); +#ifdef REMAPPING_DEBUGGING + fprintf(Q, "Rotated old keyboard out..."); + fflush(Q); +#endif /* REMAPPING_DEBUGGING */ + } + + /* End if */ + /* Copy the current keyboard definition to the 'old' keyboard */ + options.old_keyboard = options.keyboard; + + /* Make the argument to this function the 'current' keyboard */ + options.keyboard = new_keyboard; + +#ifdef REMAPPING_DEBUGGING + fprintf(Q, "Old keyboard is now %s, Current keyboard is now %s\n", + (options.old_keyboard ? "valid" : "dead"), + (options.keyboard ? "valid" : "dead")); + fflush(Q); +#endif /* REMAPPING_DEBUGGING */ + + return; +} /* End rotate_keyboard_definitions() */ + +/* This destroys the key attached as object data to a lot of the buttons + * when they get destroyed + */ +static void destroy_key_widget_data(GtkObject *object, gpointer data) +{ + KEY *key = (KEY *) data; + + if (!key) { + fprintf(stderr, + "GTKeyboard error (destroy_key_widget_data) "); + fprintf(stderr, "data == NULL!\n"); + fflush(stderr); + return; + } + /* End if */ + key = gtkeyboard_destroy_key(key); +} /* End destroy_key_widget_data() */ + +/* Call this for every widget that has a KEY structure as its object data + * this makes sure that when the widget is destroyed, destroy_key_widget_data + * gets called on the object data + */ +static void connect_destroy_signal(GtkWidget * widget, gpointer data) +{ +#if 0 + gtk_signal_connect_full(GTK_OBJECT(widget), + "destroy", + GTK_SIGNAL_FUNC(destroy_key_widget_data), + (GtkCallbackMarshal) + gtk_signal_default_marshaller, data, + (GtkDestroyNotify) destroy_key_widget_data, + FALSE, TRUE); +#endif + gtk_signal_connect(GTK_OBJECT(widget), + "destroy", + GTK_SIGNAL_FUNC(destroy_key_widget_data), + data); +} /* End connect_destroy_signal() */ + +static gint triple_callback(GtkWidget * emitter, GdkEvent * event, + gpointer data) +{ + KEY *k = (KEY *) data; + KEY *key = NO_KEY; + + if (!k) { + fprintf(stderr, + "GTKeyboard internal error: %s: NULL \"KEY\" arg.\n", + "(triple_callback)"); + fflush(stderr); + return TRUE; + } + /* End if */ + if (event->type == GDK_BUTTON_PRESS) { + key = gtkeyboard_key_copy(k); + + if (event->button.button == LEFT_MOUSE_BUTTON) { + /* Regular keypress, deal with it as normal */ + keysym_callback((GtkWidget *) NULL, + (gpointer) key); + key = gtkeyboard_destroy_key(key); + return TRUE; + } /* End if */ + else if (event->button.button == MIDDLE_MOUSE_BUTTON) { + KeySym lower, upper; + + /* Always generate the "Alt-GR" keysym */ + if (!key->alt_gr || key->alt_gr == NoSymbol) { + key->alt_gr = key->lower_case; + } /* End if */ + key->lower_case = key->upper_case = key->alt_gr; + + /* Not sure whether this is upper case or lower case. Try to + * find out by seeing if what XConvertCase returns is the same + * or different. + */ + XConvertCase(key->alt_gr, &lower, &upper); + + /* If upper is the same as alt_gr, then we need shift to be + * on. Otherwise leave it however it is + */ + if (key->alt_gr == upper) + options.SHIFT = ON; + + keysym_callback((GtkWidget *) NULL, + (gpointer) key); + /* Free the memory */ + key = gtkeyboard_destroy_key(key); + return TRUE; + } /* End else if */ + else if (event->button.button == RIGHT_MOUSE_BUTTON) { + /* Always generate the "uppercase" Keysym */ + key->lower_case = key->alt_gr = key->upper_case; + + options.SHIFT = ON; + + keysym_callback((GtkWidget *) NULL, + (gpointer) key); + key = gtkeyboard_destroy_key(key); + return TRUE; + } /* End if */ + else { + key = gtkeyboard_destroy_key(key); + return FALSE; + } /* End else */ + } + + /* End if */ + /* Tell calling code that we have not handled this event; pass it on. */ + return FALSE; +} /* End triple_callback() */ + +static void keysym_callback(GtkWidget * emitter, gpointer data) +{ + KEY *key = (KEY *) data; + KeySym sym; + char *keyname = (char *) NULL; + char *altkeyname = (char *) NULL; + KeySym lower = (KeySym) NULL, upper = (KeySym) NULL; + +#ifdef PARANOID_DEBUGGING + fprintf(Q, "keysym_callback(): Got (%s, %s, %s).\n", + XKeysymToString(key->lower_case), + XKeysymToString(key->upper_case), + XKeysymToString(key->alt_gr)); + fflush(Q); +#endif /* PARANOID_DEBUGGING */ + + /* Determine which of the syms in the KEY * structure to use. */ + keyname = XKeysymToString(key->lower_case); + altkeyname = XKeysymToString(key->alt_gr); + + if (options.ALT_GR) { + sym = key->alt_gr; + /* We have only three symbols, and we have to generate + * the fourth one for cyrillic letters. + */ + if (strstr(altkeyname, "Cyrillic_")) { + XConvertCase(sym, &lower, &upper); + + if (options.SHIFT || options.CAPS_LOCK) { + sym = upper; + } /* End if */ + } /* End if */ + } /* End if */ + else if (strstr(keyname, "KP")) { + if (options.NUMLOCK) + sym = key->upper_case; + else + sym = key->lower_case; + } /* End else if */ + else if (options.SHIFT) + sym = key->upper_case; + else if (options.CAPS_LOCK) { + if (isalpha((char) key->upper_case)) + sym = key->upper_case; + else + sym = key->lower_case; + } /* End else if */ + else + sym = key->lower_case; + + if (options.redirect_window_name) { + send_redirect_a_keysym(sym); + + } + /* End if */ + return; +} /* End keysym_callback() */ + +static int isspecial(KeySym input) +{ + char *ptr = XKeysymToString(input); + + if (input == NoSymbol || !ptr) + return (1); + if (strstr(ptr, "_L") || strstr(ptr, "_R")) + return (1); + if (strstr(ptr, "ontrol") || strstr(ptr, "Alt")) + return (1); + if (strstr(ptr, "ode")) + return (1); + if (strstr(ptr, "Tab") || strstr(ptr, "Lock") + || strstr(ptr, "pace")) + return (1); + + return (0); +} /* End isspecial */ + +GtkWidget *build_keyboard(GtkWidget * input, char *filename) +{ + /* NEW BUILD_KEYBOARD() */ + GtkWidget *mainbox = gtk_vbox_new(FALSE, 0); + GtkWidget *hbox = (GtkWidget *) NULL; + GtkWidget *button = (GtkWidget *) NULL; + char *name = (char *) NULL; + char *altname = (char *) NULL; + GtkWidget *align = (GtkWidget *) NULL; + char label[512]; + char tooltip_label[1024]; + char *errbuf = NULL; + char *utf8, *ptr; + int rowno; + int index; + char letter = '\0'; + char cyrletter = '\0'; + KEY *key; + KeySym s; + KeySym altlower, altupper; + + /* Create the current keyboard in a new place. -- This takes care of + * destroying our old ones for us. + */ + rotate_keyboard_definitions(read_keyboard_template(filename)); + + if (!options.keyboard) { + fprintf(stderr, "Couldn't read keyboard: Bummer.\n"); + fflush(stderr); + + errbuf = g_new0(char, strlen(filename) + 100); + sprintf(errbuf, + "Couldn't create keyboard from file:\n%s!\nCheck the file format!", + filename); + + button = gtk_button_new_with_label(errbuf); + + CONDFREE(errbuf); + + return (button); + } /* End if */ + else if (options.keyboard->keycount <= 0) { + errbuf = g_new0(char, strlen(filename) + 100); + sprintf(errbuf, + "Couldn't create keyboard from file:\n%s!\nCheck the file format!", + filename); + + button = gtk_button_new_with_label(errbuf); + CONDFREE(errbuf); + return (button); + } + /* End else if */ + for (rowno = 0; rowno < MAXIMUM_ROWS; rowno++) { + hbox = gtk_hbox_new(FALSE, 0); + align = gtk_alignment_new(0.5, 0.5, 0, 0); + + for (index = 0; + index < options.keyboard->row_values[rowno]; + index++) { + key = + gtkeyboard_keyboard_get_key(options.keyboard, + rowno, index); + + letter = (int) key->upper_case; + name = XKeysymToString(key->upper_case); + altname = XKeysymToString(key->alt_gr); + + if (key->upper_case == XK_Mode_switch || + key->lower_case == XK_Mode_switch) { + sprintf(label, " Alt Gr"); + } /* End if */ + else if (strstr(altname, "Cyrillic_")) { + /* We have only lower case letter, let us + * ask X to convert it to upper case. + */ + XConvertCase(key->alt_gr, &altlower, + &altupper); + + /* FIXME: Yes, this is totally wrong method to get + * the cyrillic letter. It just happen to to + * yield the right letter in koi8-r encoding. + */ + cyrletter = (char) altupper; + if (!isalpha(letter)) { + sprintf(label, " %c \n %c %c", + (char) key->upper_case, + (char) key->lower_case, + cyrletter); + } /* End if */ + else { + sprintf(label, " %c \n %c", + (char) key->upper_case, + cyrletter); + } /* End else */ + } /* End else if */ + else if ((isalnum(letter) || ispunct(letter)) + && (letter > 0)) { + if (!isalpha(letter)) + sprintf(label, " %c \n %c", + (char) key->upper_case, + (char) key->lower_case); + else + sprintf(label, " %c \n", + (char) key->upper_case); + } /* End if */ + else if (letter != 0) { + if (!iscntrl(letter) + && !isspecial(key->upper_case) + && letter != ' ') + sprintf(label, " %c \n %c", + (char) key->upper_case, + (char) key->lower_case); + else { + ptr = + string_special_cases(key-> + lower_case); + strncpy(label, ptr, 512); + g_free_(ptr); + } /* End else */ + } /* End else if */ + else { + ptr = + string_special_cases(key->lower_case); + strncpy(label, ptr, 512); + g_free_(ptr); + } /* End else */ + + s = key->lower_case; + +#if 0 + utf8 = + g_locale_to_utf8(label, -1, NULL, NULL, NULL); +#else + utf8=g_convert(label,-1,"utf-8","iso8859-1",NULL,NULL,NULL); +#endif + /* Make the correct key, and attach the correct signal + * function to it. Toggle/normal button/function + */ + if (s == XK_Caps_Lock || s == XK_Control_L || + s == XK_Control_R || s == XK_Alt_L || + s == XK_Alt_R || s == XK_Mode_switch) + button = + gtk_toggle_button_new_with_label(utf8); + else + button = gtk_button_new_with_label(utf8); + + g_free(utf8); + GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS); + if (key->code != 0) + sprintf(tooltip_label, + "KeyCode %d:\n%s\n%s\n%s", + key->code, + XKeysymToString(key->lower_case), + XKeysymToString(key->upper_case), + XKeysymToString(key->alt_gr)); + else + sprintf(tooltip_label, + "KeyCode unknown:\n%s\n%s\n%s", + XKeysymToString(key->lower_case), + XKeysymToString(key->upper_case), + XKeysymToString(key->alt_gr)); + + switch (key->lower_case) { + case XK_Caps_Lock: + gtk_signal_connect(GTK_OBJECT(button), + "clicked", + GTK_SIGNAL_FUNC + (capslock_toggle), + input); + /* Key unused in signalling */ + key = gtkeyboard_destroy_key(key); + break; + case XK_Alt_L: + case XK_Alt_R: + gtk_signal_connect(GTK_OBJECT(button), + "clicked", + GTK_SIGNAL_FUNC + (alt_toggle), NULL); + /* Key unused in signalling */ + key = gtkeyboard_destroy_key(key); + break; + case XK_Control_L: + case XK_Control_R: + gtk_signal_connect(GTK_OBJECT(button), + "clicked", + GTK_SIGNAL_FUNC + (control_toggle), NULL); + /* Key unused in signalling */ + key = gtkeyboard_destroy_key(key); + break; + case XK_Shift_L: + case XK_Shift_R: + gtk_signal_connect(GTK_OBJECT(button), + "clicked", + GTK_SIGNAL_FUNC + (shift_on), NULL); + /* Key unused in signalling */ + key = gtkeyboard_destroy_key(key); + break; + case XK_Mode_switch: + gtk_signal_connect(GTK_OBJECT(button), + "clicked", + GTK_SIGNAL_FUNC + (alt_gr_toggle), NULL); + /* Key unused in signalling */ + key = gtkeyboard_destroy_key(key); + break; + default: + gtk_signal_connect(GTK_OBJECT(button), + "button_press_event", + GTK_SIGNAL_FUNC + (triple_callback), key); + connect_destroy_signal(button, key); + break; + } /* End switch */ + + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, + FALSE, 0); + } /* End for */ + + gtk_container_add(GTK_CONTAINER(align), hbox); + gtk_box_pack_start(GTK_BOX(mainbox), align, FALSE, FALSE, + 0); + } /* End for */ + + if (filename) { + if (options.keyboard_file) { + /* We just built a complete keyboard with this file, so save its + * name for future use. + */ + /* This weird indirect freeing and copying of the string is + * due to the fact that the filename argument to this function + * may in fact be options.keyboard_file itself, so in that + * case it wouldn't be that bright to try to free it and + * copy something that's pointing to the same location. So + * instead we copy it to an intermediate spot, free the + * original, and copy the new value back. + * + * When the value actually is options.keyboard_file, we do a + * bit of redundant work, but oh well. + */ + char *tptr; + tptr = g_strdup_(filename); + g_free_(options.keyboard_file); + options.keyboard_file = g_strdup_(tptr); + g_free_(tptr); +#if 0 + fprintf(Q, + "options.keyboard_file set to be \"%s\"\n", + options.keyboard_file); + fflush(Q); +#endif + } /* End if */ + else { + /* No need to free it - just copy */ + options.keyboard_file = g_strdup_(filename); + fprintf(Q, + "options.keyboard_file set to be \"%s\"\n", + options.keyboard_file); + fflush(Q); + } /* End else */ + } + + /* End if */ + /* gtk_widget_show_all(mainbox); */ + return (mainbox); +} /* End build_keyboard() */ + +static char *string_special_cases(KeySym input) +{ + char label[1024]; + int len, x; + char *ptr; + + if (input == XK_space) { + sprintf(label, + " \n"); + } /* End if */ + else { + /* Space out the output a bit depending on string target + * length so the buttons will look right. + */ + ptr = XKeysymToString(input); + if (strlen(ptr) > 4) + sprintf(label, " %s \n", ptr); + else + sprintf(label, " %s \n", ptr); + } /* End else */ + + len = strlen(label); + + /* Special cases */ + if (strstr(label, "Control") || strstr(label, "control")) { + strcpy(label, " Ctrl "); + } /* End if */ + else if (input == XK_Mode_switch) + strcpy(label, " Alt Gr"); + else { + /* Remove the sillyness from XK_ names from the string. */ + for (x = 0; x < len; x++) { + /* Make everything uppercase */ + label[x] = toupper(label[x]); + + /* Get rid of the _R or _L that may be there. */ + if (label[x] == '_' && + (label[x + 1] == 'R' || label[x + 1] == 'L')) { + label[x] = '\0'; + } /* End if */ + } /* End for */ + } /* End else */ + + ptr = g_strdup_(label); + + return (ptr); +} /* End string_special_cases() */ + + +#define MODE_WRITE + +/* Do not allow both MODE_WRITE and MODE_APPEND to be defined. If both are + * defined, default to MODE_WRITE. The End Of Earth As We Know It (TM) occurs + * (or maybe just a compile error) MODE_WRITE is spiffier. + */ +#ifdef MODE_APPEND +# ifdef MODE_WRITE +# undef MODE_APPEND +# endif /* MODE_WRITE */ +#endif /* MODE_APPEND */ + +#ifndef __USE_GNU +# define __USE_GNU +#endif /* __USE_GNU */ + + +static void alt_gr_toggle(GtkWidget * w, gpointer data) +{ + if (ISOFF(options.ALT_GR)) { + options.ALT_GR = ON; + } /* End if */ + else { + options.ALT_GR = OFF; + } /* End else */ +} /* End alt_gr_toggle */ + +static void control_toggle(GtkWidget * w, gpointer data) +{ + if (ISOFF(options.CONTROL)) { + options.CONTROL = ON; + } /* End if */ + else { + options.CONTROL = OFF; + } /* End else */ +} /* End control_toggle */ + +static void alt_toggle(GtkWidget * w, gpointer data) +{ + if (ISOFF(options.ALT)) { + options.ALT = ON; + } /* End if */ + else { + options.ALT = OFF; + } /* End else */ +} /* End alt_toggle */ + +static void capslock_toggle(GtkWidget * w, gpointer data) +{ + if (options.redirect_window_name) + send_redirect_a_keysym(XK_Caps_Lock); + + /* Whatever it currently is, swtich it */ + if (ISOFF(options.CAPS_LOCK)) { + options.CAPS_LOCK = ON; + } /* End if */ + else { + options.CAPS_LOCK = OFF; + } /* End else */ +} /* End capslock_toggle */ + +static void shift_on(GtkWidget * w, gpointer data) +{ + /* Turn shift on */ + options.SHIFT = ON; + +} /* End shift_on */ + +/* This parses the user resource file via read_ConfigFile. This function + * doesn't actually do much other than define all of the structures for + * parsing in one place and actually doing the function calls. This is + * where you need to tweak command options. + */ +static void parse_user_resource_file(char *filename) +{ + RCVARS rc_parse_values[] = { + {"set", OPT_KEYBOARD_FILE, RC_STR, &options.keyboard_file, + NULL}, + {NULL, NULL, RC_NONE, NULL, NULL} + }; /* End rc_parse_values */ + + if (!file_exists(filename)) { + fprintf(stderr, + "Your resource file doesn't seem to exist.\n"); + fprintf(stderr, "I'll create one for you at \"%s\"\n", + filename); + fflush(stderr); + setup_default_rcfile(filename); + } + /* End if */ + read_ConfigFile(filename, rc_parse_values, 1); +} /* End parse_user_resource_file() */ + +/* Read one line from the specified file pointer and return it as + * as a pointer to char and so on. + */ +static void FILE_readline(FILE * fp, char *buffer, const int maxlen) +{ + int index = 0; + int x; + char tmp; + + do { + x = fread(&tmp, sizeof(char), 1, fp); + if ((x == 0) || (tmp == '\n')) + buffer[index] = '\0'; /* Terminate the string with a NULL */ + else + buffer[index++] = tmp; /* Add the character */ + if (!(index < maxlen)) { + fprintf(Q, + "Error on FILE_readline: index >= maxlen\n"); + fflush(Q); + return; + } /* End if */ + } while ((x != 0) && (tmp != '\n')); + return; +} /* End FILE_readline */ + +/* Copies a default rcfile from somewhere to the input filename */ +static int setup_default_rcfile(char *file) +{ + FILE *fp; + FILE *dflt; + char buffer[1024]; + char buffer2[1024]; + + if ((fp = fopen(file, "w")) == NULL) { + fprintf(Q, "Couldn't open %s for writing - cannot create ", + file); + fprintf(Q, "default .gtkeyboardrc!\n"); + fflush(Q); + return (0); + } /* End if */ + else { + /* Try to open the distribution-provided rcfile first */ + sprintf(buffer, "%s/%s", options.extrafiles, + PROVIDED_RCFILE); + + if ((dflt = fopen(buffer, "r")) == NULL) { + /* Ok, fine, so we can't open the default one we provided + * with the distribution. We'll just give the user a really + * short and crappy one. + */ + fprintf(Q, "Couldn't open %s: %s.\n", buffer, + g_strerror(errno)); + fprintf(Q, + "Fine then! We'll try something else...\n"); + fflush(Q); + + fprintf(fp, "%s", DEFAULT_RCFILE); + fclose(fp); + fprintf(Q, + "Success writing default .gtkeyboardrc\n"); + fprintf(Q, + "You can edit it to make changes later by editing "); + fprintf(Q, "%s\n", file); + fflush(Q); + return (1); + } /* End if */ + else { + while (!feof(dflt)) { + FILE_readline(dflt, buffer2, 1024); + /* Add a newline because FILE_readline kills them off */ + fprintf(fp, "%s\n", buffer2); + } /* End while */ + + fflush(fp); + fclose(dflt); + fclose(fp); + + fprintf(Q, + "Successfully wrote .gtkeyboardrc from "); + fprintf(Q, + "default. (%s: Distribution provided)\n", + PROVIDED_RCFILE); + } /* End else */ + } /* End else */ + FLUSH_EVERYTHING; + return (1); +} /* End setup_default_rcfile */ + +static int file_exists(const char *filename) +{ + struct stat file_info; + int x; + + STAT_AND_RETURN_IF_BAD(filename, x, file_info); + + return (1); +} /* End file_exists */ + +/* seems that xfree86 computes the timestamps for events like this + * strange but it relies on the *1000-32bit-wrap-around + * if anybody knows exactly how to do it, please contact me + * returns: a timestamp for the + * constructed event + */ +static Time fake_timestamp() +{ + int tint; + struct timeval tv; + struct timezone tz; /* is not used since ages */ + gettimeofday(&tv, &tz); + tint = (int) tv.tv_sec * 1000; + tint = tint / 1000 * 1000; + tint = tint + tv.tv_usec / 1000; + return ((Time) tint); +} /* End fake_timestamp() */ + +/* in: id of the root window name of the target window + * returns: id of the target window + * called by: main + */ +static Window find_window(Window top, char *name) +{ + char *wname, *iname; + XClassHint xch; + Window *children, foo; + int revert_to_return; + unsigned int nc; + + if (!strcmp(active_window_name, name)) { + XGetInputFocus(GDK_DISPLAY(), &foo, &revert_to_return); + return (foo); + } + + /* End if */ + /* First the base case */ + if (XFetchName(GDK_DISPLAY(), top, &wname)) { + if (!strncmp(wname, name, strlen(name))) { + XFree(wname); + return (top); /* found it! */ + } + /* End if */ + XFree(wname); + } + /* End if */ + if (XGetIconName(GDK_DISPLAY(), top, &iname)) { + if (!strncmp(iname, name, strlen(name))) { + XFree(iname); + return (top); /* found it! */ + } /* End if */ + XFree(iname); + } + /* End if */ + if (XGetClassHint(GDK_DISPLAY(), top, &xch)) { + if (!strcmp(xch.res_class, name)) { + XFree(xch.res_name); + XFree(xch.res_class); + return (top); /* found it! */ + } /* End if */ + XFree(xch.res_name); + XFree(xch.res_class); + } + /* End if */ + if (!XQueryTree(GDK_DISPLAY(), top, &foo, &foo, &children, &nc) || + children == NULL) + return (0); /* no more windows here */ + + /* check all the sub windows */ + for (; nc > 0; nc--) { + top = find_window(children[nc - 1], name); + if (top) + break; /* we found it somewhere */ + } /* End for */ + + /* Free that mem! Yeehaw!!! */ + if (children) + XFree(children); + + return (top); +} /* End find_window() */ + +/* This just assigns a bunch of things to certain elements of the pointer + * that are shared no matter what type of signal GTKeyboard is sending. + * Prevents code duplication in ugly places + */ +static void gtkeyboard_XEvent_common_setup(XKeyEvent * xev) +{ + xev->type = KeyPress; + xev->display = GDK_DISPLAY(); + xev->root = root; + xev->subwindow = None; + xev->time = fake_timestamp(); + xev->same_screen = True; + xev->state = 0; + xev->x = xev->y = xev->x_root = xev->y_root = 1; +} /* End gtkeyboard_XEvent_common_setup */ + +static int assign_keycode_from_keysym(KeySym foo, XKeyEvent * xev) +{ + unsigned long mask = 0; + char *keyname = (char *) NULL; + + keyname = XKeysymToString(foo); + xev->keycode = XKeysymToKeycode(GDK_DISPLAY(), foo); + + /* Check and assign masks. */ + if (options.SHIFT) { /* Need ShiftMask? */ + mask = find_modifier_mask(XKeysymToKeycode(GDK_DISPLAY(), + XK_Shift_L)); + if (!mask) { + /* WTF? Shift_L isn't mapped? OK, try Shift_R */ + mask = + find_modifier_mask(XKeysymToKeycode + (GDK_DISPLAY(), + XK_Shift_R)); + } + /* End if */ + fprintf(Q, "Shift mask: 0x%lx normal mask 0x%lx\n", mask, + (unsigned long) ShiftMask); + + /* Even if mask is actually 0 because we couldn't find the shift + * key mapped, this just won't do anything at all. + * find_modifier_mask issued a warning about not finding it, too + */ + xev->state |= mask; + options.SHIFT = 0; + } + /* End if */ + if (options.CAPS_LOCK) { /* Need LockMask? */ + mask = find_modifier_mask(XKeysymToKeycode(GDK_DISPLAY(), + XK_Caps_Lock)); + fprintf(Q, "Capslock mask: 0x%lx normal mask 0x%lx\n", + mask, (unsigned long) LockMask); + xev->state |= mask; /* Normally LockMask */ + } + /* End if */ + if (options.CONTROL) { /* Need ControlMask? */ + mask = find_modifier_mask(XKeysymToKeycode(GDK_DISPLAY(), + XK_Control_L)); + if (!mask) { + mask = + find_modifier_mask(XKeysymToKeycode + (GDK_DISPLAY(), + XK_Control_R)); + } + /* End if */ + fprintf(Q, "Control mask: 0x%lx normal mask 0x%lx\n", + mask, (unsigned long) ControlMask); + xev->state |= mask; + } + /* End if */ + if (options.ALT) { /* Need Mod1Mask? */ + mask = find_modifier_mask(XKeysymToKeycode(GDK_DISPLAY(), + XK_Alt_L)); + if (!mask) { + mask = + find_modifier_mask(XKeysymToKeycode + (GDK_DISPLAY(), XK_Alt_R)); + } + /* End if */ + fprintf(Q, "Alt mask: 0x%lx normal mask 0x%lx\n", + mask, (unsigned long) Mod1Mask); + xev->state |= mask; + } + /* End if */ + if (options.NUMLOCK) { + mask = find_modifier_mask(XKeysymToKeycode(GDK_DISPLAY(), + XK_Num_Lock)); + fprintf(Q, "Numlock: Mask 0x%lx\n", mask); + xev->state |= mask; + } + /* End if */ + if (options.ALT_GR) { + mask = find_modifier_mask(XKeysymToKeycode(GDK_DISPLAY(), + XK_Mode_switch)); + fprintf(Q, "Alt_GR: Mask 0x%lx\n", mask); + xev->state |= mask; + } + /* End if */ + if (strstr(keyname, "Cyrillic_")) { /* Cyrillic? */ + mask = find_modifier_mask(XKeysymToKeycode(GDK_DISPLAY(), + XK_ISO_Group_Shift)); + /* FIXME: How do we get this mask and what does it mean? + * Seems to be XKB_Group. Default 0x2000 + */ + + xev->state |= mask; + } + + + /* End if */ + /* xev->state = xev->state | ButtonMotionMask; */ +#if 0 + fprintf(Q, "Final mask on event: %ld 0x%lx\n", + (unsigned long) xev->state, (unsigned long) xev->state); +#endif + + if (xev->keycode != 0) + return 1; + + else + return 0; +} /* End assign_keycode_from_keysym() */ + +static void keysym_sendkey(KeySym somesym, Window w) +{ + gtkeyboard_XEvent_common_setup((XKeyEvent *) & xev); + + /* assign_keycode_from_keysym() will also add in the needed + * masks. WARNING: This may change options.SHIFT and other + * bitflags in options according to whether or not they should + * change. + */ + if (!assign_keycode_from_keysym(somesym, (XKeyEvent *) & xev)) { + return; + } + /* End if */ + xev.xkey.window = w; + + /* This may produce a BadWindow error with Xlib. Bummer. + * This happens most commonly when the window that was selected to + * redirect to doesn't exist on screen anymore. + */ + gdk_error_trap_push(); /* Catch errors, hopefully */ + + XSendEvent(GDK_DISPLAY(), w, True, KeyPressMask, &xev); + + XSync(GDK_DISPLAY(), False); + + xev.type = KeyRelease; /* Start the next Event */ + /* usleep(50000); */ +#if 0 + XFlush(GDK_DISPLAY()); +#endif + xev.xkey.time = fake_timestamp(); + XSendEvent(GDK_DISPLAY(), w, True, KeyReleaseMask, &xev); + XSync(GDK_DISPLAY(), False); + +#if 0 + gdk_flush(); +#endif + + if (gdk_error_trap_pop()) { + } + /* End if */ + return; +} /* End keysym_sendkey() */ + +/* Insert KeyCode code into slot slot in the table structure. + * Returns != 0 on success, 0 on failure. Failure means that + * the table is full, or that the code you're trying to insert is 0 + */ +static int ModmapTable_insert(ModmapTable * table, KeyCode code, int slot) +{ + int x = 0; + + if ((code == (KeyCode) 0) || (slot < 0) || (slot > 8)) + /* This operation makes no sense. Return failure. */ + return 0; + + for (x = 0; x < table->max_keypermod; x++) { + /* Insert in the first available open slot + * but not in taken slots. That would be a bad idea to + * silently overwrite some of the caller's data. :) + */ + if (table->modifiers[slot].codes[x] == 0) { + table->modifiers[slot].codes[x] = code; + return 1; + } /* End if */ + } /* End for */ + + /* Fail - can't find empty slot */ + return (0); +} /* End ModmapTable_insert() */ + +static ModmapTable *ModmapTable_new(void) +{ + XModifierKeymap *map = XGetModifierMapping(GDK_DISPLAY()); + ModmapTable *table; + int mkpm = map->max_keypermod; + int x = 0; + int y = 0; + + XFreeModifiermap(map); + table = g_new0_(ModmapTable, 1); + table->max_keypermod = mkpm; + + for (x = 0; x < 8; x++) { + for (y = 0; y < 4; y++) { + table->modifiers[x].codes[y] = (KeyCode) 0; + } /* End for */ + } /* End for */ + + return table; +} /* End ModmapTable_new() */ + +static void ModmapTable_destroy(ModmapTable * table) +{ + g_free_(table); +} /* End ModmapTable_destroy() */ + +/* Translates a string mask name into a slot number for access to numerous + * modmap data structures. + */ +static int mask_name_to_slot_number(char *maskname) +{ + char *masks[] = { "ShiftMask", "LockMask", + "ControlMask", "Mod1Mask", + "Mod2Mask", "Mod3Mask", + "Mod4Mask", "Mod5Mask" + }; + int maskcount = 8; + int y = 0; + + for (y = 0; y < maskcount; y++) { + if (g_strcasecmp(maskname, masks[y]) == 0) + return y; + } /* End for */ + + return (-1); +} /* End mask_name_to_slot_number() */ + +static unsigned long find_modifier_mask(KeyCode code) +{ + XModifierKeymap *map = XGetModifierMapping(GDK_DISPLAY()); + int x = 0, y = 0; + KeyCode c = (KeyCode) NULL; + unsigned long mask = 0; + + if (code == (KeyCode) 0) { + XFreeModifiermap(map); + fprintf(Q, + "Error finding modifier mask for 0 keycode: Have you\n"); + fprintf(Q, + "actually remapped your keyboard to this layout?\n"); + return 0; + } + /* End if */ + for (x = 0; x < 8; x++) { + for (y = 0; y < map->max_keypermod; y++) { + c = map->modifiermap[x * map->max_keypermod + y]; + if (c == code) { + XFreeModifiermap(map); + mask = slot_number_to_mask(x); + fprintf(Q, + "Found modifier %d in slot (%d,%d) mask %ld 0x%lx\n", + code, x, y, mask, mask); + return mask; + } /* End if */ + } /* End for */ + } /* End for */ + + XFreeModifiermap(map); + + /* Return nothing. This is bad, but better than doing the wrong thing. */ + fprintf(Q, + "***** WARNING: find_modifier_mask failed to locate code %d\n", + code); + fflush(Q); + return 0; +} /* End find_modifier_mask() */ + + +/* Makes a NULL-terminated string that gets passed as input nothing but + * upper case characters. + * + * THIS FUNCTION MODIFIES ITS ARGUMENT + */ +static void str_toupper(char *string) +{ + int x = 0; + + while (string[x]) { /* Stop on NULL */ + string[x] = toupper(string[x]); + x++; + } /* End while */ +} /* End str_toupper() */ + +/* Passed - the filename, the set of variables to look for, (see the header + * file for the definition of that structure) and an integer - if it's 0, + * then "syntax error" messages won't be printed. Otherwise, we'll complain. + */ +static int read_ConfigFile(char *filename, RCVARS * vars, int complain) +{ + gint n = 0; + gpointer N; + FILE *rcFile; + GHashTable *H; + gchar Line[BUFSIZ], varPrefix[BUFSIZ], varName[BUFSIZ], + varVal[BUFSIZ]; + + /* if the RC file doesn't exist, don't bother doing anything else */ + if ((rcFile = fopen(filename, "r")) == NULL) { + if (complain) { + fprintf(Q, "Couldn't open %s for reading: %s\n", + filename, g_strerror(errno)); + fflush(Q); + } /* End if */ + return 1; + } + + /* End if */ + /* Create a hash table of all the variable names and their arrayindex+1 */ + H = g_hash_table_new(g_str_hash, g_str_equal); + + n = 0; + /* Hash in all of the variable names. Later when we read them in, + * we'll hash in what we read to compare it against what was in the + * table passed to us. + */ + while (vars[n].Type != RC_NONE) { + g_hash_table_insert(H, vars[n].Name, + GINT_TO_POINTER(n + 1)); + n++; + } /* End while */ + + /* read each line of the RC file */ + while (fgets(Line, BUFSIZ, rcFile) != NULL) { + /* Strip leading and trailing whitespace from the string */ + strcpy(Line, g_strstrip(Line)); + + /* Skip comments and lines too short to have useful information + * in them. This will include "blank" lines that got g_strstrip'd + * down to 0 or 1 bytes + */ + if ((strlen(Line) < 2) || Line[0] == '#') + continue; + + /* Initialize values so in case of error they will be NULL, not + * the value of the last parse. + */ + varPrefix[0] = varName[0] = varVal[0] = '\0'; + + /* grab each variable and its value (maybe). + * If prefix is specified, it tries to read the + * given prefix. + */ + if (strstr(Line, "=")) + sscanf(Line, " %s = %s\n", varName, varVal); + else + sscanf(Line, " %s %s %s\n", varPrefix, varName, + varVal); + + /* Sometimes prefix *could* be null, but if name or value is + * null, there's really nothing we can do with that string. + */ + if (!varName[0] && !varVal[0]) { + if (complain) { + fprintf(stderr, + "Error parsing line \"%s\": ", + Line); + fprintf(stderr, + "I have no idea what that line means.\n"); + fflush(stderr); + } + /* End if */ + continue; + } + + /* End if */ + /* We want the rc file to be case insensitive, but we're looking for + * all upper-case varaible names, so convert the string to all + * upper-case so it will hash in correctly. + */ + str_toupper(varName); + + /* use the hash table to find the correct array entry */ + if ((N = g_hash_table_lookup(H, varName)) == NULL) { + continue; /* but skip to the next line if can't find it. */ + } + /* End if */ + n = GPOINTER_TO_INT(N) - 1; /* convert back into an array index */ + + /* We can't necessarily match the read prefix to the requested + * prefix since the prefixes may be different and may require + * processing through the function pointer associated with the + * record + */ + + /* + * Did we see a prefix when we didn't want one? + */ + if (!vars[n].Prefix && varPrefix[0]) { + fprintf(stderr, + "Error: Bad syntax. I wasn't expecting to see "); + fprintf(stderr, "a variable prefix on \"%s\".\n", + (varName ? varName : Line)); + fprintf(stderr, "Ignoring line \"%s\"\n", Line); + fflush(stderr); + continue; + } + + /* End if */ + /* Are we supposed to run this one through a function? */ + if (vars[n].Type == RC_PARSE_FUNC) { + /* Use the outside function specified in the structure + * to parse these line elements since their grammar is + * somewhat weird + */ + if (!vars[n]. + func(varPrefix, varName, varVal, + vars[n].Val)) { + fprintf(stderr, + "There was an error parsing \"%s\"\n", + Line); + fflush(stderr); + } + /* End if */ + continue; /* Done with this line */ + } + + /* End if */ + /* We're not supposed to run this through a function -- + * based on the variable's type, set the C variable to its saved + * value + */ + switch (vars[n].Type) { + case (RC_STR): + { + char *tok; + + /* varVal is not trustworthy, find the string + * within the quotes and use that instead. + */ + if (strstr(Line, "\"")) { + tok = strtok(Line, "\""); + if (!tok) { + /* This really shouldn't happen */ + if (complain) { + fprintf(stderr, + "Parse error within \"%s\"\n", + Line); + fflush(stderr); + } /* End if */ + break; + } /* End if */ + tok = strtok(NULL, "\""); + } /* End if */ + else + tok = &varVal[0]; + + /* free the current contents of the variable */ + if (*(gchar **) (vars[n].Val)) + g_free_(*(gchar **) vars[n].Val); + + /* set the variable to its new value. */ + if (tok) { + *(gchar **) (vars[n].Val) = + g_strdup_(tok); + } /* End if */ + else + *(gchar **) (vars[n].Val) = + (char *) NULL; + break; + } /* End block */ + } /* End switch */ + } /* End while */ + + /* clean up and exit */ + g_hash_table_destroy(H); + fclose(rcFile); + return 0; +} /* End read_ConfigFile() */ + +static KEY *gtkeyboard_key_copy(KEY * key) +{ + KEY *newkey; + + if (!key) + return (NO_KEY); + + newkey = gtkeyboard_new_key(key->lower_case, + key->upper_case, + key->alt_gr, key->aux_string); + newkey->code = key->code; + return (newkey); +} /* End gtkeyboard_key_copy() */ + +/* Returns the "keyno"th key in row "row" of keyb + * This is allocated memory which must be g_free()'d later. + * This will ALWAYS return allocated memory - it just might always be + * filled with NoSymbol + */ +static KEY *gtkeyboard_keyboard_get_key(KEYBOARD * keyb, int row, + int keyno) +{ + KEY *foobar; + int index; + int x, findex = 0; + + foobar = gtkeyboard_new_key(NoSymbol, NoSymbol, NoSymbol, NULL); + + if (row > MAXIMUM_ROWS) { + fprintf(stderr, + "gtkeyboard_keyboard_get_key: Row out of range.\n"); + fflush(stderr); + return (foobar); + } + /* End if */ + if (!keyb) { + fprintf(stderr, + "gtkeyboard_keyboard_get_key: Null keyb.\n"); + fflush(stderr); + return (foobar); + } + /* End if */ + x = findex = 0; + + while (x < row) { + /* Add up the number of keys on all lines preceeding the one we're + * looking for + */ + findex += keyb->row_values[x]; + x++; + } /* End while */ + + index = (findex * 3) + (keyno * 3); + + if (index > ((keyb->keycount * 3) - 3)) { + fprintf(stderr, "gtkeyboard_keyboard_get_key(): "); + fprintf(stderr, + "Illegal index %d of a total keycount of %d (%d).\n", + index, keyb->keycount, ((keyb->keycount * 3) - 3)); + fflush(stderr); + return (foobar); + } + + /* End if */ + /* Three consecutive KeySyms */ + foobar->lower_case = keyb->syms[index]; + foobar->upper_case = keyb->syms[(index + 1)]; + foobar->alt_gr = keyb->syms[(index + 2)]; + foobar->aux_string = (char *) NULL; /* No auxilliary */ + + if (keyb->codes) + foobar->code = keyb->codes[(findex + keyno)]; + else + foobar->code = 0; + + return (foobar); +} /* End gtkeyboard_keyboard_get_key() */ + +static KEY *gtkeyboard_new_key(const KeySym lower, const KeySym upper, + const KeySym altgr, const char *alt) +{ + KEY *somekey = g_new0_(KEY, 1); + somekey->lower_case = lower; + somekey->upper_case = upper; + somekey->alt_gr = altgr; + somekey->code = 0; + if (alt) { + somekey->aux_string = g_strdup_(alt); + } /* End if */ + else + somekey->aux_string = NULL; + + return (somekey); +} /* End gtkeyboard_new_key() */ + +static KEY *gtkeyboard_destroy_key(KEY * input) +{ + if (!input) { + fprintf(Q, + "Error: gtkeyboard_destroy_key: NULL argument.\n"); + fflush(Q); + return (NO_KEY); + } + /* End if */ + if (input->aux_string) { + g_free_(input->aux_string); + input->aux_string = (char *) NULL; + } + /* End if */ + g_free_(input); + input = NO_KEY; /* Null pointer so it won't be reused */ + + return (NO_KEY); +} /* End gtkeyboard_destroy_key() */ + +static KEYBOARD *gtkeyboard_destroy_keyboard(KEYBOARD * input) +{ + if (!input) { + fprintf(stderr, + "gtkeyboard_destroy_keyboard: Cannot destroy NULL ptr.\n"); + fflush(stderr); + return (NO_KEYBOARD); + } + /* End if */ + if (input->syms) { + g_free_(input->syms); + input->syms = NULL; + } + /* End if */ + if (input->name) { + g_free_(input->name); + input->name = NULL; + } + /* End if */ + if (input->codes) { + g_free_(input->codes); + input->codes = NULL; + } + /* End if */ + if (input->modmap) { + ModmapTable_destroy(input->modmap); + input->modmap = (ModmapTable *) NULL; + } + /* End if */ + if (input->trans) { + CONDFREE(input->trans->space); + CONDFREE(input->trans->tab); + CONDFREE(input->trans->alt_gr); + CONDFREE(input->trans->alt); + CONDFREE(input->trans->control); + CONDFREE(input->trans->shift); + CONDFREE(input->trans->backspace); + + /* Free the parent pointer */ + CONDFREE(input->trans); + } + /* End if */ + g_free_(input); + input = NO_KEYBOARD; + + return (NO_KEYBOARD); +} /* End gtkeyboard_destroy_keyboard() */ + +static int get_valid_line(FILE * fp, char *mem, const int maxlen) +{ + mem[0] = '\0'; + + while (!feof(fp) && mem[0] == '\0') { + FILE_readline(fp, mem, maxlen); + + if (mem[0] != '\0') { + strcpy(mem, g_strstrip(mem)); + + if ((mem[0] && mem[0] == '#') + || (mem[0] && mem[0] == '!')) + mem[0] = '\0'; + } /* End if */ + } /* End while */ + + if (mem[0] == '\0') + return (0); + + else + return (1); +} /* End get_valid_line() */ + +/* Parses the contents of a keyboard description file and returns + * a corresponding KEYBOARD structure. + */ +static KEYBOARD *read_keyboard_template(char *filename) +{ + KEYBOARD *keyb = NO_KEYBOARD; + FILE *fp; + register int x = 0, y = 0; + int line_size = 1024; + int index = 0; + char linedata[line_size]; + char **tokens; + char **tofree; + char *ptr; + + if (!filename || !file_exists(filename)) { + fprintf(stderr, "Error loading keyboard file \"%s\": ", + (filename ? filename : "NULL")); + fprintf(stderr, "File doesn't exist."); + fflush(stderr); + return (NO_KEYBOARD); + } + /* End if */ + fp = fopen(filename, "r"); + + if (!fp) { + return (NO_KEYBOARD); + } + /* End if */ + linedata[0] = '\0'; + + if (!get_valid_line(fp, linedata, line_size)) { + fclose(fp); + return (NO_KEYBOARD); + } + /* End if */ + keyb = g_new0_(KEYBOARD, 1); + keyb->modmap = ModmapTable_new(); + + tofree = g_strsplit(linedata, " ", -1); + tokens = tofree; + + keyb->keycount = 0; + keyb->trans = g_new_(KeyboardTranslation, 1); + + /* Initialize it's various elements */ + keyb->trans->shift = keyb->trans->backspace = keyb->trans->space = + keyb->trans->caps_lock = keyb->trans->control = + keyb->trans->tab = keyb->trans->alt = keyb->trans->alt_gr = + (char *) NULL; + + for (x = 0; x < MAXIMUM_ROWS; x++) { + if (*tokens) + ptr = *tokens++; + else + ptr = NULL; + + if (ptr) + keyb->row_values[x] = atoi(ptr); + else { + *tokens = NULL; + keyb->row_values[x] = 0; + } /* End else */ + + keyb->keycount += keyb->row_values[x]; + } /* End for */ + + g_strfreev(tofree); + tofree = tokens = NULL; + ptr = NULL; + + /* We now know how many keys we have to allocate, how many lines to read, + * and all that good stuff. + * + * Each key must have 3 syms, (lower case, upper case, and Alt Gr) + * So allocate 3*keyb->keycount items, and read keyb->keycount lines. + */ + keyb->syms = g_new0_(KeySym, (3 * keyb->keycount)); + keyb->codes = g_new0_(KeyCode, keyb->keycount); + keyb->name = g_strdup_(filename); /* Save the name of the keyboard */ + + for (x = 0; x < keyb->keycount; x++) { + keyb->codes[x] = 0; /* Initialize that keycode since we're already + * paying the price of the loop and it needs + * to be done. + */ + + if (!get_valid_line(fp, linedata, line_size)) { + fprintf(stderr, + "Error reading file %s: Bad line %d.\n", + filename, (x + 1)); + fflush(stderr); + fflush(stderr); + keyb = gtkeyboard_destroy_keyboard(keyb); + fclose(fp); + return (NO_KEYBOARD); + } + /* End if */ + tokens = tofree = g_strsplit(linedata, " ", -1); + + for (y = 0; y < 3; y++) { + if (*tokens) + ptr = *tokens++; + else + ptr = NULL; + + index = (x * 3) + y; + + if (ptr) { + /* Translate a string into a KeySym */ + keyb->syms[index] = XStringToKeysym(ptr); + + /* Error check that KeySym */ + if (!keyb->syms[index] + || keyb->syms[index] == NoSymbol) { + keyb->syms[index] = NoSymbol; + keyb = + gtkeyboard_destroy_keyboard + (keyb); + return (NO_KEYBOARD); + } /* End if */ + } /* End if */ + else { + /* This kinda sucks */ + keyb->syms[index] = NoSymbol; + } /* End else */ + } /* End for */ + + if (ptr) { + ptr = *tokens++; + } + + /* End if */ + /* Grab the KeyCode if it's there */ + keyb->codes[x] = atoi(ptr ? ptr : "0"); + + if (ptr) { + ptr = *tokens++; + } + /* End if */ + if (ptr && strcmp(ptr, "") != 0) { +#if 0 + fprintf(Q, "Reading proposed mask \"%s\"\n", ptr); + fflush(Q); +#endif + + if (!ModmapTable_insert + (keyb->modmap, keyb->codes[x], + mask_name_to_slot_number(ptr))) { + fprintf(Q, + "*** Warning: Failed to insert %d into %d\n", + keyb->codes[x], + mask_name_to_slot_number(ptr)); + } + /* End if */ +#if 0 + fprintf(Q, "Inserted code %d in slot %d\n", + keyb->codes[x], + mask_name_to_slot_number(ptr)); + fflush(Q); +#endif + } + /* End if */ + g_strfreev(tofree); + } /* End for */ + + fclose(fp); + + return (keyb); +} /* End read_keyboard_template() */ + +static void send_redirect_a_keysym(KeySym input) +{ + Window window; + int revert_to; + + if (!options.other || options.other == (Window) NULL) { + /* SEND_TO_BOTH_WINDOWS was probably set and there wasn't + * a redirect window to send to. Let's save the time involved + * with doing all this string crap and just jump out here. + */ + return; + } + /* End if */ + if (options.other) { + /* send to window user picked */ + keysym_sendkey(input, options.other); + } /* End if */ + else { + /* default to just send the event to whatever window has the input + * focus + */ + XGetInputFocus(GDK_DISPLAY(), &window, &revert_to); + keysym_sendkey(input, window); + } /* End else */ +} /* End send_redirect_a_keysym() */ + +gint track_focus(gpointer data) +{ + Window winFocus; + Window wfcopy; + int revert_to_return; + char *winName; + + + /* find out which window currently has focus */ + XGetInputFocus(GDK_DISPLAY(), &winFocus, &revert_to_return); + wfcopy = winFocus; + + /* Return if the window is the same or if it's the program + * window or if we can't redirect to that window. + * + * If there was a previous window that was any good, stick to + * that one. + */ + if (winFocus == options.redirect_window || + winFocus == GUI.xwindow || + winFocus == None || winFocus == PointerRoot) { + return TRUE; + } + + + + /* End if */ + /* At this point, we know the window is "good" and that we want + * it's name. We're going to use it as the redirect from now on. + */ + /* set up error trapping, in case we get a BadWindow error */ + gdk_error_trap_push(); + + /* this could generate the error */ + XFetchName(GDK_DISPLAY(), winFocus, &winName); + if (!winName) + winName = "Unknown"; + + gdk_flush(); + + if (gdk_error_trap_pop()) { + /* Oops...error. Probably BadWindow */ + CONDFREE(options.redirect_window_name); + + options.redirect_window = None; /* reset focus window */ + options.other = None; + + printf + ("There was an error finding a valid redirect window.\n"); + return TRUE; /* better luck next time */ + } + + /* End if */ + /* since we made it this far, update the window_name */ + if (winName) { + CONDFREE(options.redirect_window_name); + + /* Grab the window definition */ + options.redirect_window = wfcopy; + options.other = wfcopy; + + options.redirect_window_name = g_strdup_(winName); + } /* End if */ + return TRUE; +} /* End track_focus */ |