diff --git a/Makefile b/Makefile index a3781204..372a96ea 100644 --- a/Makefile +++ b/Makefile @@ -24,12 +24,16 @@ normal: debug: $(CC) -o simpleswitcher-debug simpleswitcher.c -std=c99 $(CFLAGS) -Wunused-parameter -g -DDEBUG $(LDADD) -install: install-man +install: normal install-man install -Dm 755 simpleswitcher $(BINDIR)/simpleswitcher install-man: install -Dm 644 simpleswitcher.1 $(MANDIR) - gzip $(MANDIR)/simpleswitcher.1 + gzip -f $(MANDIR)/simpleswitcher.1 clean: rm -f simpleswitcher simpleswitcher-debug + + +indent: + @astyle --style=linux -S -C -D -N -H -L -W3 -f simpleswitcher.c textbox.c diff --git a/simpleswitcher.c b/simpleswitcher.c index 1b61fe82..b93b192e 100644 --- a/simpleswitcher.c +++ b/simpleswitcher.c @@ -1,10 +1,10 @@ /** * simpleswitcher - * + * * MIT/X11 License * Copyright (c) 2012 Sean Pringle * Modified 2013 Qball Cow - * + * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including @@ -12,10 +12,10 @@ * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: - * + * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. @@ -23,7 +23,7 @@ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * + * */ #define _GNU_SOURCE @@ -58,53 +58,55 @@ #define OPAQUE 0xffffffff #define OPACITY "_NET_WM_WINDOW_OPACITY" -static void* allocate(unsigned long bytes) +static void* allocate( unsigned long bytes ) { - void *ptr = malloc(bytes); - if (!ptr) - { - fprintf(stderr, "malloc failed!\n"); - exit(EXIT_FAILURE); - } - return ptr; + void *ptr = malloc( bytes ); + + if ( !ptr ) { + fprintf( stderr, "malloc failed!\n" ); + exit( EXIT_FAILURE ); + } + + return ptr; } -static void* allocate_clear(unsigned long bytes) +static void* allocate_clear( unsigned long bytes ) { - void *ptr = allocate(bytes); - memset(ptr, 0, bytes); - return ptr; + void *ptr = allocate( bytes ); + memset( ptr, 0, bytes ); + return ptr; } -static void* reallocate(void *ptr, unsigned long bytes) +static void* reallocate( void *ptr, unsigned long bytes ) { - ptr = realloc(ptr, bytes); - if (!ptr) - { - fprintf(stderr, "realloc failed!\n"); - exit(EXIT_FAILURE); - } - return ptr; + ptr = realloc( ptr, bytes ); + + if ( !ptr ) { + fprintf( stderr, "realloc failed!\n" ); + exit( EXIT_FAILURE ); + } + + return ptr; } -static inline char **tokenize(const char *input) +static inline char **tokenize( const char *input ) { - if(input == NULL) return NULL; + if ( input == NULL ) return NULL; + char *saveptr = NULL, *token; char **retv = NULL; // First entry is always full (modified) stringtext. int num_tokens = 1; //First entry is string that is modified. - retv = malloc(2*sizeof(char*)); - retv[0] = strdup(input); + retv = malloc( 2*sizeof( char* ) ); + retv[0] = strdup( input ); retv[1] = NULL; // Iterate over tokens. - for( - token = strtok_r(retv[0], " ", &saveptr); - token != NULL; - token = strtok_r(NULL, " ", &saveptr)) - { - retv = realloc(retv, sizeof(char*)*(num_tokens+2)); + for ( + token = strtok_r( retv[0], " ", &saveptr ); + token != NULL; + token = strtok_r( NULL, " ", &saveptr ) ) { + retv = realloc( retv, sizeof( char* )*( num_tokens+2 ) ); retv[num_tokens+1] = NULL; retv[num_tokens] = token; num_tokens++; @@ -113,63 +115,70 @@ static inline char **tokenize(const char *input) return retv; } -static inline void tokenize_free(char **ip) +static inline void tokenize_free( char **ip ) { - if(ip == NULL) return; - if(ip[0]) - free(ip[0]); - free(ip); + if ( ip == NULL ) return; + + if ( ip[0] ) + free( ip[0] ); + + free( ip ); } -void catch_exit(__attribute__((unused)) int sig) +void catch_exit( __attribute__( ( unused ) ) int sig ) { - while (0 < waitpid(-1, NULL, WNOHANG)); + while ( 0 < waitpid( -1, NULL, WNOHANG ) ); } -int execsh(char *cmd) +int execsh( char *cmd ) { - // use sh for args parsing - return execlp("/bin/sh", "sh", "-c", cmd, NULL); + // use sh for args parsing + return execlp( "/bin/sh", "sh", "-c", cmd, NULL ); } // execute sub-process -pid_t exec_cmd(char *cmd) +pid_t exec_cmd( char *cmd ) { - if (!cmd || !cmd[0]) return -1; - signal(SIGCHLD, catch_exit); - pid_t pid = fork(); - if (!pid) - { - setsid(); - execsh(cmd); - exit(EXIT_FAILURE); - } - return pid; + if ( !cmd || !cmd[0] ) return -1; + + signal( SIGCHLD, catch_exit ); + pid_t pid = fork(); + + if ( !pid ) { + setsid(); + execsh( cmd ); + exit( EXIT_FAILURE ); + } + + return pid; } // cli arg handling -static int find_arg(const int argc, char * const argv[], const char * const key) +static int find_arg( const int argc, char * const argv[], const char * const key ) { - int i; - for (i = 0; i < argc && strcasecmp(argv[i], key); i++); + int i; + + for ( i = 0; i < argc && strcasecmp( argv[i], key ); i++ ); + return i < argc ? i: -1; } -static char* find_arg_str(int argc, char *argv[], char *key, char* def) +static char* find_arg_str( int argc, char *argv[], char *key, char* def ) { - int i = find_arg(argc, argv, key); - return (i > 0 && i < argc-1) ? argv[i+1]: def; + int i = find_arg( argc, argv, key ); + return ( i > 0 && i < argc-1 ) ? argv[i+1]: def; } -static int find_arg_int(int argc, char *argv[], char *key, int def) +static int find_arg_int( int argc, char *argv[], char *key, int def ) { - int i = find_arg(argc, argv, key); - return (i > 0 && i < argc-1) ? strtol(argv[i+1], NULL, 10): def; + int i = find_arg( argc, argv, key ); + return ( i > 0 && i < argc-1 ) ? strtol( argv[i+1], NULL, 10 ): def; } unsigned int NumlockMask = 0; -Display *display; Screen *screen; +Display *display; +Screen *screen; Window root; int screen_id; -static int (*xerror)(Display *, XErrorEvent *); +static int ( *xerror )( Display *, XErrorEvent * ); #define ATOM_ENUM(x) x #define ATOM_CHAR(x) #x @@ -217,32 +226,33 @@ static int (*xerror)(Display *, XErrorEvent *); X(_NET_WM_DESKTOP),\ X(_NET_SUPPORTED) -enum { EWMH_ATOMS(ATOM_ENUM), NETATOMS }; -const char *netatom_names[] = { EWMH_ATOMS(ATOM_CHAR) }; +enum { EWMH_ATOMS( ATOM_ENUM ), NETATOMS }; +const char *netatom_names[] = { EWMH_ATOMS( ATOM_CHAR ) }; Atom netatoms[NETATOMS]; // X error handler -int oops(__attribute__((unused)) Display *d, XErrorEvent *ee) +int oops( __attribute__( ( unused ) ) Display *d, XErrorEvent *ee ) { - if (ee->error_code == BadWindow - || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) - || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) - ) return 0; - fprintf(stderr, "error: request code=%d, error code=%d\n", ee->request_code, ee->error_code); - return xerror(display, ee); + if ( ee->error_code == BadWindow + || ( ee->request_code == X_GrabButton && ee->error_code == BadAccess ) + || ( ee->request_code == X_GrabKey && ee->error_code == BadAccess ) + ) return 0; + + fprintf( stderr, "error: request code=%d, error code=%d\n", ee->request_code, ee->error_code ); + return xerror( display, ee ); } // usable space on a monitor typedef struct { - int x, y, w, h; - int l, r, t, b; + int x, y, w, h; + int l, r, t, b; } workarea; // window lists typedef struct { - Window *array; - void **data; - int len; + Window *array; + void **data; + int len; } winlist; winlist *cache_client; @@ -255,52 +265,65 @@ winlist *cache_xattr; winlist* winlist_new() { - winlist *l = allocate(sizeof(winlist)); l->len = 0; - l->array = allocate(sizeof(Window) * (WINLIST+1)); - l->data = allocate(sizeof(void*) * (WINLIST+1)); - return l; + winlist *l = allocate( sizeof( winlist ) ); + l->len = 0; + l->array = allocate( sizeof( Window ) * ( WINLIST+1 ) ); + l->data = allocate( sizeof( void* ) * ( WINLIST+1 ) ); + return l; } -int winlist_append(winlist *l, Window w, void *d) +int winlist_append( winlist *l, Window w, void *d ) { - if (l->len > 0 && !(l->len % WINLIST)) - { - l->array = reallocate(l->array, sizeof(Window) * (l->len+WINLIST+1)); - l->data = reallocate(l->data, sizeof(void*) * (l->len+WINLIST+1)); - } - l->data[l->len] = d; - l->array[l->len++] = w; - return l->len-1; + if ( l->len > 0 && !( l->len % WINLIST ) ) { + l->array = reallocate( l->array, sizeof( Window ) * ( l->len+WINLIST+1 ) ); + l->data = reallocate( l->data, sizeof( void* ) * ( l->len+WINLIST+1 ) ); + } + + l->data[l->len] = d; + l->array[l->len++] = w; + return l->len-1; } -void winlist_empty(winlist *l) +void winlist_empty( winlist *l ) { - while (l->len > 0) free(l->data[--(l->len)]); + while ( l->len > 0 ) free( l->data[--( l->len )] ); } -void winlist_free(winlist *l) +void winlist_free( winlist *l ) { - winlist_empty(l); free(l->array); free(l->data); free(l); + winlist_empty( l ); + free( l->array ); + free( l->data ); + free( l ); } -void winlist_empty_2d(winlist *l) +void winlist_empty_2d( winlist *l ) { - while (l->len > 0) winlist_free(l->data[--(l->len)]); + while ( l->len > 0 ) winlist_free( l->data[--( l->len )] ); } -int winlist_find(winlist *l, Window w) +int winlist_find( winlist *l, Window w ) { - // iterate backwards. theory is: windows most often accessed will be - // nearer the end. testing with kcachegrind seems to support this... - int i; Window o; winlist_descend(l, i, o) if (w == o) return i; - return -1; + // iterate backwards. theory is: windows most often accessed will be + // nearer the end. testing with kcachegrind seems to support this... + int i; + Window o; + + winlist_descend( l, i, o ) if ( w == o ) return i; + + return -1; } -int winlist_forget(winlist *l, Window w) +int winlist_forget( winlist *l, Window w ) { - int i, j; - for (i = 0, j = 0; i < l->len; i++, j++) - { - l->array[j] = l->array[i]; - l->data[j] = l->data[i]; - if (l->array[i] == w) { free(l->data[i]); j--; } - } - l->len -= (i-j); - return j != i ?1:0; + int i, j; + + for ( i = 0, j = 0; i < l->len; i++, j++ ) { + l->array[j] = l->array[i]; + l->data[j] = l->data[i]; + + if ( l->array[i] == w ) { + free( l->data[i] ); + j--; + } + } + + l->len -= ( i-j ); + return j != i ?1:0; } #define CLIENTTITLE 100 @@ -310,12 +333,12 @@ int winlist_forget(winlist *l, Window w) // a managable window typedef struct { - Window window, trans; - XWindowAttributes xattr; - char title[CLIENTTITLE], class[CLIENTCLASS], name[CLIENTNAME]; - int states; - Atom state[CLIENTSTATE], type; - workarea monitor; + Window window, trans; + XWindowAttributes xattr; + char title[CLIENTTITLE], class[CLIENTCLASS], name[CLIENTNAME]; + int states; + Atom state[CLIENTSTATE], type; + workarea monitor; } client; #define MENUXFTFONT "mono-14" @@ -348,750 +371,808 @@ unsigned int config_zeltak_mode; unsigned int config_i3_mode; // allocate a pixel value for an X named color -static unsigned int color_get(const char *const name) +static unsigned int color_get( const char *const name ) { - XColor color; - Colormap map = DefaultColormap(display, screen_id); - return XAllocNamedColor(display, map, name, &color, &color) ? color.pixel: None; + XColor color; + Colormap map = DefaultColormap( display, screen_id ); + return XAllocNamedColor( display, map, name, &color, &color ) ? color.pixel: None; } // find mouse pointer location -int pointer_get(Window root, int *x, int *y) +int pointer_get( Window root, int *x, int *y ) { - *x = 0; *y = 0; - Window rr, cr; int rxr, ryr, wxr, wyr; unsigned int mr; - if (XQueryPointer(display, root, &rr, &cr, &rxr, &ryr, &wxr, &wyr, &mr)) - { - *x = rxr; *y = ryr; - return 1; - } - return 0; + *x = 0; + *y = 0; + Window rr, cr; + int rxr, ryr, wxr, wyr; + unsigned int mr; + + if ( XQueryPointer( display, root, &rr, &cr, &rxr, &ryr, &wxr, &wyr, &mr ) ) { + *x = rxr; + *y = ryr; + return 1; + } + + return 0; } -int take_keyboard(Window w) +int take_keyboard( Window w ) { - int i; - for (i = 0; i < 1000; i++) - { - if (XGrabKeyboard(display, w, True, GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess) - return 1; - usleep(1000); - } - return 0; + int i; + + for ( i = 0; i < 1000; i++ ) { + if ( XGrabKeyboard( display, w, True, GrabModeAsync, GrabModeAsync, CurrentTime ) == GrabSuccess ) + return 1; + + usleep( 1000 ); + } + + return 0; } void release_keyboard() { - XUngrabKeyboard(display, CurrentTime); + XUngrabKeyboard( display, CurrentTime ); } // XGetWindowAttributes with caching -XWindowAttributes* window_get_attributes(Window w) +XWindowAttributes* window_get_attributes( Window w ) { - int idx = winlist_find(cache_xattr, w); - if (idx < 0) - { - XWindowAttributes *cattr = allocate(sizeof(XWindowAttributes)); - if (XGetWindowAttributes(display, w, cattr)) - { - winlist_append(cache_xattr, w, cattr); - return cattr; - } - free(cattr); - return NULL; - } - return cache_xattr->data[idx]; + int idx = winlist_find( cache_xattr, w ); + + if ( idx < 0 ) { + XWindowAttributes *cattr = allocate( sizeof( XWindowAttributes ) ); + + if ( XGetWindowAttributes( display, w, cattr ) ) { + winlist_append( cache_xattr, w, cattr ); + return cattr; + } + + free( cattr ); + return NULL; + } + + return cache_xattr->data[idx]; } // retrieve a property of any type from a window -int window_get_prop(Window w, Atom prop, Atom *type, int *items, void *buffer, unsigned int bytes) +int window_get_prop( Window w, Atom prop, Atom *type, int *items, void *buffer, unsigned int bytes ) { - Atom _type; if (!type) type = &_type; - int _items; if (!items) items = &_items; - int format; unsigned long nitems, nbytes; unsigned char *ret = NULL; - memset(buffer, 0, bytes); + Atom _type; - if (XGetWindowProperty(display, w, prop, 0, bytes/4, False, AnyPropertyType, type, - &format, &nitems, &nbytes, &ret) == Success && ret && *type != None && format) - { - if (format == 8) memmove(buffer, ret, MIN(bytes, nitems)); - if (format == 16) memmove(buffer, ret, MIN(bytes, nitems * sizeof(short))); - if (format == 32) memmove(buffer, ret, MIN(bytes, nitems * sizeof(long))); - *items = (int)nitems; XFree(ret); - return 1; - } - return 0; + if ( !type ) type = &_type; + + int _items; + + if ( !items ) items = &_items; + + int format; + unsigned long nitems, nbytes; + unsigned char *ret = NULL; + memset( buffer, 0, bytes ); + + if ( XGetWindowProperty( display, w, prop, 0, bytes/4, False, AnyPropertyType, type, + &format, &nitems, &nbytes, &ret ) == Success && ret && *type != None && format ) { + if ( format == 8 ) memmove( buffer, ret, MIN( bytes, nitems ) ); + + if ( format == 16 ) memmove( buffer, ret, MIN( bytes, nitems * sizeof( short ) ) ); + + if ( format == 32 ) memmove( buffer, ret, MIN( bytes, nitems * sizeof( long ) ) ); + + *items = ( int )nitems; + XFree( ret ); + return 1; + } + + return 0; } // retrieve a text property from a window // technically we could use window_get_prop(), but this is better for character set support -char* window_get_text_prop(Window w, Atom atom) +char* window_get_text_prop( Window w, Atom atom ) { - XTextProperty prop; char *res = NULL; - char **list = NULL; int count; - if (XGetTextProperty(display, w, &prop, atom) && prop.value && prop.nitems) - { - if (prop.encoding == XA_STRING) - { - res = allocate(strlen((char*)prop.value)+1); - strcpy(res, (char*)prop.value); - } - else - if (XmbTextPropertyToTextList(display, &prop, &list, &count) >= Success && count > 0 && *list) - { - res = allocate(strlen(*list)+1); - strcpy(res, *list); - XFreeStringList(list); - } - } - if (prop.value) XFree(prop.value); - return res; + XTextProperty prop; + char *res = NULL; + char **list = NULL; + int count; + + if ( XGetTextProperty( display, w, &prop, atom ) && prop.value && prop.nitems ) { + if ( prop.encoding == XA_STRING ) { + res = allocate( strlen( ( char* )prop.value )+1 ); + strcpy( res, ( char* )prop.value ); + } else if ( XmbTextPropertyToTextList( display, &prop, &list, &count ) >= Success && count > 0 && *list ) { + res = allocate( strlen( *list )+1 ); + strcpy( res, *list ); + XFreeStringList( list ); + } + } + + if ( prop.value ) XFree( prop.value ); + + return res; } -int window_get_atom_prop(Window w, Atom atom, Atom *list, int count) +int window_get_atom_prop( Window w, Atom atom, Atom *list, int count ) { - Atom type; int items; - return window_get_prop(w, atom, &type, &items, list, count*sizeof(Atom)) && type == XA_ATOM ? items:0; + Atom type; + int items; + return window_get_prop( w, atom, &type, &items, list, count*sizeof( Atom ) ) && type == XA_ATOM ? items:0; } -void window_set_atom_prop(Window w, Atom prop, Atom *atoms, int count) +void window_set_atom_prop( Window w, Atom prop, Atom *atoms, int count ) { - XChangeProperty(display, w, prop, XA_ATOM, 32, PropModeReplace, (unsigned char*)atoms, count); + XChangeProperty( display, w, prop, XA_ATOM, 32, PropModeReplace, ( unsigned char* )atoms, count ); } -int window_get_cardinal_prop(Window w, Atom atom, unsigned long *list, int count) +int window_get_cardinal_prop( Window w, Atom atom, unsigned long *list, int count ) { - Atom type; int items; - return window_get_prop(w, atom, &type, &items, list, count*sizeof(unsigned long)) && type == XA_CARDINAL ? items:0; + Atom type; + int items; + return window_get_prop( w, atom, &type, &items, list, count*sizeof( unsigned long ) ) && type == XA_CARDINAL ? items:0; } // a ClientMessage -int window_send_message(Window target, Window subject, Atom atom, unsigned long protocol, unsigned long mask, Time time) +int window_send_message( Window target, Window subject, Atom atom, unsigned long protocol, unsigned long mask, Time time ) { - XEvent e; memset(&e, 0, sizeof(XEvent)); - e.xclient.type = ClientMessage; - e.xclient.message_type = atom; e.xclient.window = subject; - e.xclient.data.l[0] = protocol; e.xclient.data.l[1] = time; - e.xclient.send_event = True; e.xclient.format = 32; - int r = XSendEvent(display, target, False, mask, &e) ?1:0; - XFlush(display); - return r; + XEvent e; + memset( &e, 0, sizeof( XEvent ) ); + e.xclient.type = ClientMessage; + e.xclient.message_type = atom; + e.xclient.window = subject; + e.xclient.data.l[0] = protocol; + e.xclient.data.l[1] = time; + e.xclient.send_event = True; + e.xclient.format = 32; + int r = XSendEvent( display, target, False, mask, &e ) ?1:0; + XFlush( display ); + return r; } // find the dimensions of the monitor displaying point x,y -void monitor_dimensions(Screen *screen, int x, int y, workarea *mon) +void monitor_dimensions( Screen *screen, int x, int y, workarea *mon ) { - memset(mon, 0, sizeof(workarea)); - mon->w = WidthOfScreen(screen); - mon->h = HeightOfScreen(screen); + memset( mon, 0, sizeof( workarea ) ); + mon->w = WidthOfScreen( screen ); + mon->h = HeightOfScreen( screen ); - // locate the current monitor - if (XineramaIsActive(display)) - { - int monitors, i; - XineramaScreenInfo *info = XineramaQueryScreens(display, &monitors); - if (info) for (i = 0; i < monitors; i++) - { - if (INTERSECT(x, y, 1, 1, info[i].x_org, info[i].y_org, info[i].width, info[i].height)) - { - mon->x = info[i].x_org; mon->y = info[i].y_org; - mon->w = info[i].width; mon->h = info[i].height; - break; - } - } - XFree(info); - } + // locate the current monitor + if ( XineramaIsActive( display ) ) { + int monitors, i; + XineramaScreenInfo *info = XineramaQueryScreens( display, &monitors ); + + if ( info ) for ( i = 0; i < monitors; i++ ) { + if ( INTERSECT( x, y, 1, 1, info[i].x_org, info[i].y_org, info[i].width, info[i].height ) ) { + mon->x = info[i].x_org; + mon->y = info[i].y_org; + mon->w = info[i].width; + mon->h = info[i].height; + break; + } + } + + XFree( info ); + } } // determine which monitor holds the active window, or failing that the mouse pointer -void monitor_active(workarea *mon) +void monitor_active( workarea *mon ) { - Window root = RootWindow(display, XScreenNumberOfScreen(screen)); + Window root = RootWindow( display, XScreenNumberOfScreen( screen ) ); - unsigned long id; Atom type; int count; - if (window_get_prop(root, netatoms[_NET_ACTIVE_WINDOW], &type, &count, &id, 1) - && type == XA_WINDOW && count > 0) - { - XWindowAttributes *attr = window_get_attributes(id); - monitor_dimensions(screen, attr->x, attr->y, mon); - return; - } - int x, y; - if (pointer_get(root, &x, &y)) - { - monitor_dimensions(screen, x, y, mon); - return; - } - monitor_dimensions(screen, 0, 0, mon); + unsigned long id; + Atom type; + int count; + + if ( window_get_prop( root, netatoms[_NET_ACTIVE_WINDOW], &type, &count, &id, 1 ) + && type == XA_WINDOW && count > 0 ) { + XWindowAttributes *attr = window_get_attributes( id ); + monitor_dimensions( screen, attr->x, attr->y, mon ); + return; + } + + int x, y; + + if ( pointer_get( root, &x, &y ) ) { + monitor_dimensions( screen, x, y, mon ); + return; + } + + monitor_dimensions( screen, 0, 0, mon ); } // _NET_WM_STATE_* -int client_has_state(client *c, Atom state) +int client_has_state( client *c, Atom state ) { - int i; - for (i = 0; i < c->states; i++) - if (c->state[i] == state) return 1; - return 0; + int i; + + for ( i = 0; i < c->states; i++ ) + if ( c->state[i] == state ) return 1; + + return 0; } // collect info on any window // doesn't have to be a window we'll end up managing -client* window_client(Window win) +client* window_client( Window win ) { - if (win == None) return NULL; - int idx = winlist_find(cache_client, win); - if (idx >= 0) return cache_client->data[idx]; + if ( win == None ) return NULL; - // if this fails, we're up that creek - XWindowAttributes *attr = window_get_attributes(win); - if (!attr) return NULL; + int idx = winlist_find( cache_client, win ); - client *c = allocate_clear(sizeof(client)); - c->window = win; - // copy xattr so we don't have to care when stuff is freed - memmove(&c->xattr, attr, sizeof(XWindowAttributes)); - XGetTransientForHint(display, win, &c->trans); + if ( idx >= 0 ) return cache_client->data[idx]; - c->states = window_get_atom_prop(win, netatoms[_NET_WM_STATE], c->state, CLIENTSTATE); - window_get_atom_prop(win, netatoms[_NET_WM_WINDOW_TYPE], &c->type, 1); + // if this fails, we're up that creek + XWindowAttributes *attr = window_get_attributes( win ); - if (c->type == None) c->type = (c->trans != None) - // trasients default to dialog - ? netatoms[_NET_WM_WINDOW_TYPE_DIALOG] - // non-transients default to normal - : netatoms[_NET_WM_WINDOW_TYPE_NORMAL]; + if ( !attr ) return NULL; - char *name; - if ((name = window_get_text_prop(c->window, netatoms[_NET_WM_NAME])) && name) - { - snprintf(c->title, CLIENTTITLE, "%s", name); - free(name); - } - else - if (XFetchName(display, c->window, &name)) - { - snprintf(c->title, CLIENTTITLE, "%s", name); - XFree(name); - } - XClassHint chint; - if (XGetClassHint(display, c->window, &chint)) - { - snprintf(c->class, CLIENTCLASS, "%s", chint.res_class); - snprintf(c->name, CLIENTNAME, "%s", chint.res_name); - XFree(chint.res_class); XFree(chint.res_name); - } - monitor_dimensions(c->xattr.screen, c->xattr.x, c->xattr.y, &c->monitor); - winlist_append(cache_client, c->window, c); - return c; + client *c = allocate_clear( sizeof( client ) ); + c->window = win; + // copy xattr so we don't have to care when stuff is freed + memmove( &c->xattr, attr, sizeof( XWindowAttributes ) ); + XGetTransientForHint( display, win, &c->trans ); + + c->states = window_get_atom_prop( win, netatoms[_NET_WM_STATE], c->state, CLIENTSTATE ); + window_get_atom_prop( win, netatoms[_NET_WM_WINDOW_TYPE], &c->type, 1 ); + + if ( c->type == None ) c->type = ( c->trans != None ) + // trasients default to dialog + ? netatoms[_NET_WM_WINDOW_TYPE_DIALOG] + // non-transients default to normal + : netatoms[_NET_WM_WINDOW_TYPE_NORMAL]; + + char *name; + + if ( ( name = window_get_text_prop( c->window, netatoms[_NET_WM_NAME] ) ) && name ) { + snprintf( c->title, CLIENTTITLE, "%s", name ); + free( name ); + } else if ( XFetchName( display, c->window, &name ) ) { + snprintf( c->title, CLIENTTITLE, "%s", name ); + XFree( name ); + } + + XClassHint chint; + + if ( XGetClassHint( display, c->window, &chint ) ) { + snprintf( c->class, CLIENTCLASS, "%s", chint.res_class ); + snprintf( c->name, CLIENTNAME, "%s", chint.res_name ); + XFree( chint.res_class ); + XFree( chint.res_name ); + } + + monitor_dimensions( c->xattr.screen, c->xattr.x, c->xattr.y, &c->monitor ); + winlist_append( cache_client, c->window, c ); + return c; } #define ALLWINDOWS 1 #define DESKTOPWINDOWS 2 -unsigned int windows_modmask; KeySym windows_keysym; +unsigned int windows_modmask; +KeySym windows_keysym; // flags to set if we switch modes on the fly int run_windows = 0; Window main_window = None; #include "textbox.c" -void menu_draw(textbox *text, textbox **boxes, int max_lines, int selected, char **filtered) +void menu_draw( textbox *text, textbox **boxes, int max_lines, int selected, char **filtered ) { - int i; - textbox_draw(text); - for (i = 0; i < max_lines; i++) - { - textbox_font(boxes[i], config_menu_font, - i == selected ? config_menu_hlfg: config_menu_fg, - i == selected ? config_menu_hlbg: config_menu_bg); - textbox_text(boxes[i], filtered[i] ? filtered[i]: ""); - textbox_draw(boxes[i]); - } + int i; + textbox_draw( text ); + + for ( i = 0; i < max_lines; i++ ) { + textbox_font( boxes[i], config_menu_font, + i == selected ? config_menu_hlfg: config_menu_fg, + i == selected ? config_menu_hlbg: config_menu_bg ); + textbox_text( boxes[i], filtered[i] ? filtered[i]: "" ); + textbox_draw( boxes[i] ); + } } /* Very bad implementation of tab completion. * It will complete to the common prefix */ -static int calculate_common_prefix(char **filtered, int max_lines) +static int calculate_common_prefix( char **filtered, int max_lines ) { int length_prefix = 0,j,found = 1; - if(filtered[0] != NULL) { + + if ( filtered[0] != NULL ) { char *p = filtered[0]; - do{ + + do { found = 1; - for(j=0; j < max_lines && filtered[j] != NULL; j++) { - if(filtered[j][length_prefix] == '\0' || filtered[j][length_prefix] != *p) { - if(found) + + for ( j=0; j < max_lines && filtered[j] != NULL; j++ ) { + if ( filtered[j][length_prefix] == '\0' || filtered[j][length_prefix] != *p ) { + if ( found ) found=0; + break; } } - if(found) + + if ( found ) length_prefix++; + p++; - }while(found ); + } while ( found ); } + return length_prefix; } -int menu(char **lines, char **input, char *prompt, int selected, Time *time) +int menu( char **lines, char **input, char *prompt, int selected, Time *time ) { - int line = -1, i, j, chosen = 0, aborted = 0; - workarea mon; monitor_active(&mon); + int line = -1, i, j, chosen = 0, aborted = 0; + workarea mon; + monitor_active( &mon ); - int num_lines = 0; for (; lines[num_lines]; num_lines++); - int max_lines = MIN(config_menu_lines, num_lines); - selected = MAX(MIN(num_lines-1, selected), 0); + int num_lines = 0; - int w = config_menu_width < 101 ? (mon.w/100)*config_menu_width: config_menu_width; - int x = mon.x + (mon.w - w)/2; + for ( ; lines[num_lines]; num_lines++ ); - Window box; - XWindowAttributes attr; + int max_lines = MIN( config_menu_lines, num_lines ); + selected = MAX( MIN( num_lines-1, selected ), 0 ); - // main window isn't explicitly destroyed in case we switch modes. Reusing it prevents flicker - if (main_window != None && XGetWindowAttributes(display, main_window, &attr)) - { - box = main_window; - } - else - { - box = XCreateSimpleWindow(display, root, x, 0, w, 300, 1, color_get(config_menu_bc), color_get(config_menu_bg)); - XSelectInput(display, box, ExposureMask); - // make it an unmanaged window - window_set_atom_prop(box, netatoms[_NET_WM_STATE], &netatoms[_NET_WM_STATE_ABOVE], 1); - //window_set_atom_prop(box, netatoms[_NET_WM_WINDOW_TYPE], &netatoms[_NET_WM_WINDOW_TYPE_DOCK], 1); - XSetWindowAttributes sattr; sattr.override_redirect = True; - XChangeWindowAttributes(display, box, CWOverrideRedirect, &sattr); - main_window = box; + int w = config_menu_width < 101 ? ( mon.w/100 )*config_menu_width: config_menu_width; + int x = mon.x + ( mon.w - w )/2; + + Window box; + XWindowAttributes attr; + + // main window isn't explicitly destroyed in case we switch modes. Reusing it prevents flicker + if ( main_window != None && XGetWindowAttributes( display, main_window, &attr ) ) { + box = main_window; + } else { + box = XCreateSimpleWindow( display, root, x, 0, w, 300, 1, color_get( config_menu_bc ), color_get( config_menu_bg ) ); + XSelectInput( display, box, ExposureMask ); + // make it an unmanaged window + window_set_atom_prop( box, netatoms[_NET_WM_STATE], &netatoms[_NET_WM_STATE_ABOVE], 1 ); + //window_set_atom_prop(box, netatoms[_NET_WM_WINDOW_TYPE], &netatoms[_NET_WM_WINDOW_TYPE_DOCK], 1); + XSetWindowAttributes sattr; + sattr.override_redirect = True; + XChangeWindowAttributes( display, box, CWOverrideRedirect, &sattr ); + main_window = box; // Set the WM_NAME - XStoreName(display, box, "simpleswitcher"); + XStoreName( display, box, "simpleswitcher" ); // Hack to set window opacity. - unsigned int opacity_set = (unsigned int)((config_window_opacity/100.0)* OPAQUE); - XChangeProperty(display, box, XInternAtom(display, OPACITY, False), - XA_CARDINAL, 32, PropModeReplace, - (unsigned char *) &opacity_set, 1L); + unsigned int opacity_set = ( unsigned int )( ( config_window_opacity/100.0 )* OPAQUE ); + XChangeProperty( display, box, XInternAtom( display, OPACITY, False ), + XA_CARDINAL, 32, PropModeReplace, + ( unsigned char * ) &opacity_set, 1L ); } - // search text input - textbox *text = textbox_create(box, TB_AUTOHEIGHT|TB_EDITABLE, 5, 5, w-10, 1, - config_menu_font, config_menu_fg, config_menu_bg, "", prompt); - textbox_show(text); + // search text input + textbox *text = textbox_create( box, TB_AUTOHEIGHT|TB_EDITABLE, 5, 5, w-10, 1, + config_menu_font, config_menu_fg, config_menu_bg, "", prompt ); + textbox_show( text ); - int line_height = text->font->ascent + text->font->descent; - line_height += line_height/10; + int line_height = text->font->ascent + text->font->descent; + line_height += line_height/10; - // filtered list display - textbox **boxes = allocate_clear(sizeof(textbox*) * max_lines); + // filtered list display + textbox **boxes = allocate_clear( sizeof( textbox* ) * max_lines ); - for (i = 0; i < max_lines; i++) - { - boxes[i] = textbox_create(box, TB_AUTOHEIGHT, 5, (i+1) * line_height + 5, w-10, 1, - config_menu_font, config_menu_fg, config_menu_bg, lines[i], NULL); - textbox_show(boxes[i]); - } + for ( i = 0; i < max_lines; i++ ) { + boxes[i] = textbox_create( box, TB_AUTOHEIGHT, 5, ( i+1 ) * line_height + 5, w-10, 1, + config_menu_font, config_menu_fg, config_menu_bg, lines[i], NULL ); + textbox_show( boxes[i] ); + } - // filtered list - char **filtered = allocate_clear(sizeof(char*) * max_lines); - int *line_map = allocate_clear(sizeof(int) * max_lines); - int filtered_lines = 0; + // filtered list + char **filtered = allocate_clear( sizeof( char* ) * max_lines ); + int *line_map = allocate_clear( sizeof( int ) * max_lines ); + int filtered_lines = 0; int jin = 0; - for (i = 0; i < max_lines; i++) - { + + for ( i = 0; i < max_lines; i++ ) { filtered[jin] = lines[i]; - line_map[jin] = i; + line_map[jin] = i; jin++; filtered_lines++; - } + } - // resize window vertically to suit - int h = line_height * (max_lines+1) + 8; - int y = mon.y + (mon.h - h)/2; - XMoveResizeWindow(display, box, x, y, w, h); - XMapRaised(display, box); + // resize window vertically to suit + int h = line_height * ( max_lines+1 ) + 8; + int y = mon.y + ( mon.h - h )/2; + XMoveResizeWindow( display, box, x, y, w, h ); + XMapRaised( display, box ); - take_keyboard(box); - for (;;) - { - XEvent ev; - XNextEvent(display, &ev); + take_keyboard( box ); - if (ev.type == Expose) - { - while (XCheckTypedEvent(display, Expose, &ev)); - menu_draw(text, boxes, max_lines, selected, filtered); - } - else - if (ev.type == KeyPress) - { - while (XCheckTypedEvent(display, KeyPress, &ev)); + for ( ;; ) { + XEvent ev; + XNextEvent( display, &ev ); - if (time) - *time = ev.xkey.time; + if ( ev.type == Expose ) { + while ( XCheckTypedEvent( display, Expose, &ev ) ); - int rc = textbox_keypress(text, &ev); - if (rc < 0) - { - chosen = 1; - break; - } - else - if (rc) - { - char **tokens = tokenize(text->text); - // input changed - for (i = 0, j = 0; i < num_lines && j < max_lines; i++) - { + menu_draw( text, boxes, max_lines, selected, filtered ); + } else if ( ev.type == KeyPress ) { + while ( XCheckTypedEvent( display, KeyPress, &ev ) ); + + if ( time ) + *time = ev.xkey.time; + + int rc = textbox_keypress( text, &ev ); + + if ( rc < 0 ) { + chosen = 1; + break; + } else if ( rc ) { + char **tokens = tokenize( text->text ); + + // input changed + for ( i = 0, j = 0; i < num_lines && j < max_lines; i++ ) { int match = 1; - // Do a tokenized match. - if(tokens) for(int j = 1; match && tokens[j]; j++) - { - match = (strcasestr(lines[i], tokens[j]) != NULL); - } - // If each token was matched, add it to list. - if(match) - { + // Do a tokenized match. + if ( tokens ) for ( int j = 1; match && tokens[j]; j++ ) { + match = ( strcasestr( lines[i], tokens[j] ) != NULL ); + } + + // If each token was matched, add it to list. + if ( match ) { line_map[j] = i; filtered[j++] = lines[i]; } } + // Cleanup + bookkeeping. filtered_lines = j; - selected = MAX(0, MIN(selected, j-1)); - for (; j < max_lines; j++) - filtered[j] = NULL; - if(config_zeltak_mode && filtered_lines == 1){ + selected = MAX( 0, MIN( selected, j-1 ) ); + + for ( ; j < max_lines; j++ ) + filtered[j] = NULL; + + if ( config_zeltak_mode && filtered_lines == 1 ) { chosen = 1; break; } - tokenize_free(tokens); - } - else - { - // unhandled key - KeySym key = XkbKeycodeToKeysym(display, ev.xkey.keycode, 0, 0); - if (key == XK_Escape - // pressing one of the global key bindings closes the switcher. this allows fast closing of the menu if an item is not selected - || ((windows_modmask == AnyModifier || ev.xkey.state & windows_modmask) && key == windows_keysym) - ) - { + tokenize_free( tokens ); + } else { + // unhandled key + KeySym key = XkbKeycodeToKeysym( display, ev.xkey.keycode, 0, 0 ); + + if ( key == XK_Escape + // pressing one of the global key bindings closes the switcher. this allows fast closing of the menu if an item is not selected + || ( ( windows_modmask == AnyModifier || ev.xkey.state & windows_modmask ) && key == windows_keysym ) + ) { aborted = 1; break; } - else - // Up or Shift-Tab - if (key == XK_Up || (key == XK_Tab && ev.xkey.state & ShiftMask)) - selected = selected ? MAX(0, selected-1): MAX(0, filtered_lines-1); + else + + // Up or Shift-Tab + if ( key == XK_Up || ( key == XK_Tab && ev.xkey.state & ShiftMask ) ) + selected = selected ? MAX( 0, selected-1 ): MAX( 0, filtered_lines-1 ); - else - // Down or Tab - if (key == XK_Down || key == XK_Tab) - { - if(filtered_lines == 1) { - chosen = 1; - break; - } - int length_prefix = calculate_common_prefix(filtered, max_lines); - if(length_prefix && strncasecmp(filtered[0], text->text, length_prefix)) { - // Do not want to modify original string, so make copy. - // not eff.. - char * str = strndup(filtered[0], length_prefix); - textbox_text(text, str); - textbox_cursor_end(text); - free(str); - } else - selected = selected < filtered_lines-1 ? MIN(filtered_lines-1, selected+1): 0; - } + + // Down or Tab + if ( key == XK_Down || key == XK_Tab ) { + if ( filtered_lines == 1 ) { + chosen = 1; + break; + } + + int length_prefix = calculate_common_prefix( filtered, max_lines ); + + if ( length_prefix && strncasecmp( filtered[0], text->text, length_prefix ) ) { + // Do not want to modify original string, so make copy. + // not eff.. + char * str = strndup( filtered[0], length_prefix ); + textbox_text( text, str ); + textbox_cursor_end( text ); + free( str ); + } else + selected = selected < filtered_lines-1 ? MIN( filtered_lines-1, selected+1 ): 0; + } } - menu_draw(text, boxes, max_lines, selected, filtered); - } - } - release_keyboard(); - if (chosen && filtered[selected]) - line = line_map[selected]; + menu_draw( text, boxes, max_lines, selected, filtered ); + } + } - if (line < 0 && !aborted && input) - *input = strdup(text->text); + release_keyboard(); - textbox_free(text); - for (i = 0; i < max_lines; i++) - textbox_free(boxes[i]); + if ( chosen && filtered[selected] ) + line = line_map[selected]; - free(boxes); + if ( line < 0 && !aborted && input ) + *input = strdup( text->text ); - free(filtered); - free(line_map); + textbox_free( text ); - return line; + for ( i = 0; i < max_lines; i++ ) + textbox_free( boxes[i] ); + + free( boxes ); + + free( filtered ); + free( line_map ); + + return line; } #define FORK 1 #define NOFORK 2 -void run_switcher(int fmode) +void run_switcher( int fmode ) { - // TODO: this whole function is messy. build a nicer solution + // TODO: this whole function is messy. build a nicer solution - // we fork because it's technically possible to have multiple window - // lists up at once on a zaphod multihead X setup. - // this also happens to isolate the Xft font stuff in a child process - // that gets cleaned up every time. that library shows some valgrind - // strangeness... - if (fmode == FORK) - { - if (fork()) return; - display = XOpenDisplay(0); - XSync(display, True); - } + // we fork because it's technically possible to have multiple window + // lists up at once on a zaphod multihead X setup. + // this also happens to isolate the Xft font stuff in a child process + // that gets cleaned up every time. that library shows some valgrind + // strangeness... + if ( fmode == FORK ) { + if ( fork() ) return; - do { + display = XOpenDisplay( 0 ); + XSync( display, True ); + } - char pattern[50], **list = NULL; - int i, plen = 0, lines = 0; + do { + + char pattern[50], **list = NULL; + int i, plen = 0, lines = 0; unsigned int classfield = 0; - unsigned long desktops = 0, current_desktop = 0; - Window w; client *c; + unsigned long desktops = 0, current_desktop = 0; + Window w; + client *c; - // windows we actually display. may be slightly different to _NET_CLIENT_LIST_STACKING - // if we happen to have a window destroyed while we're working... - winlist *ids = winlist_new(); + // windows we actually display. may be slightly different to _NET_CLIENT_LIST_STACKING + // if we happen to have a window destroyed while we're working... + winlist *ids = winlist_new(); - if (!window_get_cardinal_prop(root, netatoms[_NET_CURRENT_DESKTOP], ¤t_desktop, 1)) - current_desktop = 0; + if ( !window_get_cardinal_prop( root, netatoms[_NET_CURRENT_DESKTOP], ¤t_desktop, 1 ) ) + current_desktop = 0; + + // find window list + Atom type; + int nwins; + unsigned long *wins = allocate_clear( sizeof( unsigned long ) * 100 ); + + if ( window_get_prop( root, netatoms[_NET_CLIENT_LIST_STACKING], &type, &nwins, wins, 100 * sizeof( unsigned long ) ) + && type == XA_WINDOW ) { + // calc widths of fields + for ( i = nwins-1; i > -1; i-- ) { + if ( ( c = window_client( wins[i] ) ) + && !c->xattr.override_redirect + && !client_has_state( c, netatoms[_NET_WM_STATE_SKIP_PAGER] ) + && !client_has_state( c, netatoms[_NET_WM_STATE_SKIP_TASKBAR] ) ) { + classfield = MAX( classfield, strlen( c->class ) ); - // find window list - Atom type; int nwins; unsigned long *wins = allocate_clear(sizeof(unsigned long) * 100); - if (window_get_prop(root, netatoms[_NET_CLIENT_LIST_STACKING], &type, &nwins, wins, 100 * sizeof(unsigned long)) - && type == XA_WINDOW) - { - // calc widths of fields - for (i = nwins-1; i > -1; i--) - { - if ((c = window_client(wins[i])) - && !c->xattr.override_redirect - && !client_has_state(c, netatoms[_NET_WM_STATE_SKIP_PAGER]) - && !client_has_state(c, netatoms[_NET_WM_STATE_SKIP_TASKBAR])) - { - classfield = MAX(classfield, strlen(c->class)); // In i3 mode, skip the i3bar completely. - if(config_i3_mode && strstr(c->class, "i3bar") != NULL) continue; - winlist_append(ids, c->window, NULL); - } - } + if ( config_i3_mode && strstr( c->class, "i3bar" ) != NULL ) continue; - if (!window_get_cardinal_prop(root, netatoms[_NET_NUMBER_OF_DESKTOPS], &desktops, 1)) + winlist_append( ids, c->window, NULL ); + } + } + + if ( !window_get_cardinal_prop( root, netatoms[_NET_NUMBER_OF_DESKTOPS], &desktops, 1 ) ) desktops = 1; - plen += sprintf(pattern+plen, "%%-%ds %%s", MAX(5, classfield)); - list = allocate_clear(sizeof(char*) * (ids->len+1)); lines = 0; + plen += sprintf( pattern+plen, "%%-%ds %%s", MAX( 5, classfield ) ); + list = allocate_clear( sizeof( char* ) * ( ids->len+1 ) ); + lines = 0; - // build the actual list - winlist_ascend(ids, i, w) - { - if ((c = window_client(w))) - { + // build the actual list + winlist_ascend( ids, i, w ) { + if ( ( c = window_client( w ) ) ) { // final line format - char *line = allocate(strlen(c->title) + strlen(c->class) + classfield + 50); + char *line = allocate( strlen( c->title ) + strlen( c->class ) + classfield + 50 ); + + sprintf( line, pattern, c->class, c->title ); - sprintf(line, pattern, c->class, c->title); - list[lines++] = line; } - } - char *input = NULL; - Time time; - int n = menu(list, &input, "> ", 1, &time); - if (n >= 0 && list[n]) - { - if(config_i3_mode) - { + } + char *input = NULL; + Time time; + int n = menu( list, &input, "> ", 1, &time ); + + if ( n >= 0 && list[n] ) { + if ( config_i3_mode ) { // Hack for i3. char array[128]; - snprintf(array,128,"i3-msg [id=\"%d\"] focus",(int)(ids->array[n])); - printf("Executing: %s\n", array); - exec_cmd(array); - } - else - { - if (isdigit(list[n][0])) - { + snprintf( array,128,"i3-msg [id=\"%d\"] focus",( int )( ids->array[n] ) ); + printf( "Executing: %s\n", array ); + exec_cmd( array ); + } else { + if ( isdigit( list[n][0] ) ) { // TODO: get rid of strtol - window_send_message(root, root, netatoms[_NET_CURRENT_DESKTOP], strtol(list[n], NULL, 10)-1, - SubstructureNotifyMask | SubstructureRedirectMask, time); - XSync(display, False); + window_send_message( root, root, netatoms[_NET_CURRENT_DESKTOP], strtol( list[n], NULL, 10 )-1, + SubstructureNotifyMask | SubstructureRedirectMask, time ); + XSync( display, False ); } - window_send_message(root, ids->array[n], netatoms[_NET_ACTIVE_WINDOW], 2, // 2 = pager - SubstructureNotifyMask | SubstructureRedirectMask, time); - } - } - else - // act as a launcher - if (input) - { - exec_cmd(input); - } - for (i = 0; i < lines; i++) - free(list[i]); - free(list); - } - free(wins); - winlist_free(ids); - } - while (run_windows ); - if (fmode == FORK) - exit(EXIT_SUCCESS); + window_send_message( root, ids->array[n], netatoms[_NET_ACTIVE_WINDOW], 2, // 2 = pager + SubstructureNotifyMask | SubstructureRedirectMask, time ); + } + } else + + // act as a launcher + if ( input ) { + exec_cmd( input ); + } + + for ( i = 0; i < lines; i++ ) + free( list[i] ); + + free( list ); + } + + free( wins ); + winlist_free( ids ); + } while ( run_windows ); + + if ( fmode == FORK ) + exit( EXIT_SUCCESS ); } // KeyPress event -void handle_keypress(XEvent *ev) +void handle_keypress( XEvent *ev ) { - KeySym key = XkbKeycodeToKeysym(display, ev->xkey.keycode, 0, 0); + KeySym key = XkbKeycodeToKeysym( display, ev->xkey.keycode, 0, 0 ); - if ((windows_modmask == AnyModifier || ev->xkey.state & windows_modmask) && key == windows_keysym) - run_switcher(FORK); + if ( ( windows_modmask == AnyModifier || ev->xkey.state & windows_modmask ) && key == windows_keysym ) + run_switcher( FORK ); } // convert a Mod+key arg to mod mask and keysym -void parse_key(char *combo, unsigned int *mod, KeySym *key) +void parse_key( char *combo, unsigned int *mod, KeySym *key ) { - unsigned int modmask = 0; - if (strcasestr(combo, "shift")) modmask |= ShiftMask; - if (strcasestr(combo, "control")) modmask |= ControlMask; - if (strcasestr(combo, "mod1")) modmask |= Mod1Mask; - if (strcasestr(combo, "mod2")) modmask |= Mod2Mask; - if (strcasestr(combo, "mod3")) modmask |= Mod3Mask; - if (strcasestr(combo, "mod4")) modmask |= Mod4Mask; - if (strcasestr(combo, "mod5")) modmask |= Mod5Mask; - *mod = modmask ? modmask: AnyModifier; + unsigned int modmask = 0; - char i = strlen(combo); - while (i > 0 && !strchr("-+", combo[i-1])) i--; + if ( strcasestr( combo, "shift" ) ) modmask |= ShiftMask; - KeySym sym = XStringToKeysym(combo+i); - if (sym == NoSymbol || (!modmask && (strchr(combo, '-') || strchr(combo, '+')))) - { - fprintf(stderr, "sorry, cannot understand key combination: %s\n", combo); - exit(EXIT_FAILURE); - } - *key = sym; + if ( strcasestr( combo, "control" ) ) modmask |= ControlMask; + + if ( strcasestr( combo, "mod1" ) ) modmask |= Mod1Mask; + + if ( strcasestr( combo, "mod2" ) ) modmask |= Mod2Mask; + + if ( strcasestr( combo, "mod3" ) ) modmask |= Mod3Mask; + + if ( strcasestr( combo, "mod4" ) ) modmask |= Mod4Mask; + + if ( strcasestr( combo, "mod5" ) ) modmask |= Mod5Mask; + + *mod = modmask ? modmask: AnyModifier; + + char i = strlen( combo ); + + while ( i > 0 && !strchr( "-+", combo[i-1] ) ) i--; + + KeySym sym = XStringToKeysym( combo+i ); + + if ( sym == NoSymbol || ( !modmask && ( strchr( combo, '-' ) || strchr( combo, '+' ) ) ) ) { + fprintf( stderr, "sorry, cannot understand key combination: %s\n", combo ); + exit( EXIT_FAILURE ); + } + + *key = sym; } // bind a key combination on a root window, compensating for Lock* states -void grab_key(unsigned int modmask, KeySym key) +void grab_key( unsigned int modmask, KeySym key ) { - KeyCode keycode = XKeysymToKeycode(display, key); - XUngrabKey(display, keycode, AnyModifier, root); + KeyCode keycode = XKeysymToKeycode( display, key ); + XUngrabKey( display, keycode, AnyModifier, root ); - if (modmask != AnyModifier) - { - // bind to combinations of mod and lock masks, so caps and numlock don't confuse people - XGrabKey(display, keycode, modmask, root, True, GrabModeAsync, GrabModeAsync); - XGrabKey(display, keycode, modmask|LockMask, root, True, GrabModeAsync, GrabModeAsync); - if (NumlockMask) - { - XGrabKey(display, keycode, modmask|NumlockMask, root, True, GrabModeAsync, GrabModeAsync); - XGrabKey(display, keycode, modmask|NumlockMask|LockMask, root, True, GrabModeAsync, GrabModeAsync); - } - } - else - { - // nice simple single key bind - XGrabKey(display, keycode, AnyModifier, root, True, GrabModeAsync, GrabModeAsync); - } + if ( modmask != AnyModifier ) { + // bind to combinations of mod and lock masks, so caps and numlock don't confuse people + XGrabKey( display, keycode, modmask, root, True, GrabModeAsync, GrabModeAsync ); + XGrabKey( display, keycode, modmask|LockMask, root, True, GrabModeAsync, GrabModeAsync ); + + if ( NumlockMask ) { + XGrabKey( display, keycode, modmask|NumlockMask, root, True, GrabModeAsync, GrabModeAsync ); + XGrabKey( display, keycode, modmask|NumlockMask|LockMask, root, True, GrabModeAsync, GrabModeAsync ); + } + } else { + // nice simple single key bind + XGrabKey( display, keycode, AnyModifier, root, True, GrabModeAsync, GrabModeAsync ); + } } -int main(int argc, char *argv[]) +int main( int argc, char *argv[] ) { - int i, j; + int i, j; - // catch help request - if (find_arg(argc, argv, "-help") >= 0 - || find_arg(argc, argv, "--help") >= 0 - || find_arg(argc, argv, "-h") >= 0) - { - fprintf(stderr, "See the man page or visit http://github.com/DaveDavenport/simpleswitcher\n"); - fprintf(stderr, "Original code can be found: http://github.com/seanpringle/simpleswitcher\n"); - return EXIT_FAILURE; - } - if(!(display = XOpenDisplay(0))) - { - fprintf(stderr, "cannot open display!\n"); - return EXIT_FAILURE; - } - signal(SIGCHLD, catch_exit); - screen = DefaultScreenOfDisplay(display); - screen_id = DefaultScreen(display); - root = DefaultRootWindow(display); - XSync(display, False); xerror = XSetErrorHandler(oops); XSync(display, False); + // catch help request + if ( find_arg( argc, argv, "-help" ) >= 0 + || find_arg( argc, argv, "--help" ) >= 0 + || find_arg( argc, argv, "-h" ) >= 0 ) { + fprintf( stderr, "See the man page or visit http://github.com/DaveDavenport/simpleswitcher\n" ); + fprintf( stderr, "Original code can be found: http://github.com/seanpringle/simpleswitcher\n" ); + return EXIT_FAILURE; + } - // determine numlock mask so we can bind on keys with and without it - XModifierKeymap *modmap = XGetModifierMapping(display); - for (i = 0; i < 8; i++) - for (j = 0; j < (int)modmap->max_keypermod; j++) - if (modmap->modifiermap[i*modmap->max_keypermod+j] == XKeysymToKeycode(display, XK_Num_Lock)) - NumlockMask = (1<= 0); - config_i3_mode = (find_arg(ac, av, "-i3") >= 0); + for ( i = 0; i < 8; i++ ) + for ( j = 0; j < ( int )modmap->max_keypermod; j++ ) + if ( modmap->modifiermap[i*modmap->max_keypermod+j] == XKeysymToKeycode( display, XK_Num_Lock ) ) + NumlockMask = ( 1<= 0) - { - run_switcher(NOFORK); - exit(EXIT_SUCCESS); - } + XFreeModifiermap( modmap ); - // in background mode from here on + int ac = argc; + char **av = argv; + cache_client = winlist_new(); + cache_xattr = winlist_new(); - // key combination to display all windows from all desktops - parse_key(find_arg_str(ac, av, "-key", "F12"), &windows_modmask, &windows_keysym); + // X atom values + for ( i = 0; i < NETATOMS; i++ ) netatoms[i] = XInternAtom( display, netatom_names[i], False ); + + config_menu_width = find_arg_int( ac, av, "-width", MENUWIDTH ); + config_menu_lines = find_arg_int( ac, av, "-lines", MENULINES ); + config_menu_font = find_arg_str( ac, av, "-font", MENUXFTFONT ); + config_menu_fg = find_arg_str( ac, av, "-fg", MENUFG ); + config_menu_bg = find_arg_str( ac, av, "-bg", MENUBG ); + config_menu_bgalt = find_arg_str( ac, av, "-bgalt", MENUBGALT ); + config_menu_hlfg = find_arg_str( ac, av, "-hlfg", MENUHLFG ); + config_menu_hlbg = find_arg_str( ac, av, "-hlbg", MENUHLBG ); + config_menu_bc = find_arg_str( ac, av, "-bc", MENUBC ); + config_menu_bw = find_arg_int( ac, av, "-bw", 1 ); + config_window_opacity = find_arg_int( ac, av, "-o", 100 ); + + config_zeltak_mode = ( find_arg( ac, av, "-zeltak" ) >= 0 ); + config_i3_mode = ( find_arg( ac, av, "-i3" ) >= 0 ); + + // flags to run immediately and exit + if ( find_arg( ac, av, "-now" ) >= 0 ) { + run_switcher( NOFORK ); + exit( EXIT_SUCCESS ); + } + + // in background mode from here on + + // key combination to display all windows from all desktops + parse_key( find_arg_str( ac, av, "-key", "F12" ), &windows_modmask, &windows_keysym ); - // bind key combos - grab_key(windows_modmask, windows_keysym); + // bind key combos + grab_key( windows_modmask, windows_keysym ); - XEvent ev; - for (;;) - { - // caches only live for a single event - winlist_empty(cache_xattr); - winlist_empty(cache_client); + XEvent ev; - // block and wait for something - XNextEvent(display, &ev); - if (ev.xany.window == None) continue; + for ( ;; ) { + // caches only live for a single event + winlist_empty( cache_xattr ); + winlist_empty( cache_client ); - if (ev.type == KeyPress) handle_keypress(&ev); - } - return EXIT_SUCCESS; + // block and wait for something + XNextEvent( display, &ev ); + + if ( ev.xany.window == None ) continue; + + if ( ev.type == KeyPress ) handle_keypress( &ev ); + } + + return EXIT_SUCCESS; } diff --git a/textbox.c b/textbox.c index 788d23e7..3f8557b9 100644 --- a/textbox.c +++ b/textbox.c @@ -32,343 +32,333 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define TB_EDITABLE 1<<19 typedef struct { - unsigned long flags; - Window window, parent; - short x, y, w, h; - short cursor; - XftFont *font; - XftColor color_fg, color_bg; - char *text, *prompt; - XIM xim; - XIC xic; - XGlyphInfo extents; + unsigned long flags; + Window window, parent; + short x, y, w, h; + short cursor; + XftFont *font; + XftColor color_fg, color_bg; + char *text, *prompt; + XIM xim; + XIC xic; + XGlyphInfo extents; } textbox; -void textbox_font(textbox *tb, char *font, char *fg, char *bg); -void textbox_text(textbox *tb, char *text); -void textbox_moveresize(textbox *tb, int x, int y, int w, int h); +void textbox_font( textbox *tb, char *font, char *fg, char *bg ); +void textbox_text( textbox *tb, char *text ); +void textbox_moveresize( textbox *tb, int x, int y, int w, int h ); // Xft text box, optionally editable -textbox* textbox_create(Window parent, unsigned long flags, short x, short y, short w, short h, char *font, char *fg, char *bg, char *text, char *prompt) +textbox* textbox_create( Window parent, unsigned long flags, short x, short y, short w, short h, char *font, char *fg, char *bg, char *text, char *prompt ) { - textbox *tb = calloc(1, sizeof(textbox)); + textbox *tb = calloc( 1, sizeof( textbox ) ); - tb->flags = flags; - tb->parent = parent; + tb->flags = flags; + tb->parent = parent; - tb->x = x; tb->y = y; tb->w = MAX(1, w); tb->h = MAX(1, h); + tb->x = x; + tb->y = y; + tb->w = MAX( 1, w ); + tb->h = MAX( 1, h ); - XColor color; Colormap map = DefaultColormap(display, DefaultScreen(display)); - unsigned int cp = XAllocNamedColor(display, map, bg, &color, &color) ? color.pixel: None; + XColor color; + Colormap map = DefaultColormap( display, DefaultScreen( display ) ); + unsigned int cp = XAllocNamedColor( display, map, bg, &color, &color ) ? color.pixel: None; - tb->window = XCreateSimpleWindow(display, tb->parent, tb->x, tb->y, tb->w, tb->h, 0, None, cp); + tb->window = XCreateSimpleWindow( display, tb->parent, tb->x, tb->y, tb->w, tb->h, 0, None, cp ); - // need to preload the font to calc line height - textbox_font(tb, font, fg, bg); + // need to preload the font to calc line height + textbox_font( tb, font, fg, bg ); - tb->prompt = strdup(prompt ? prompt: ""); - textbox_text(tb, text ? text: ""); + tb->prompt = strdup( prompt ? prompt: "" ); + textbox_text( tb, text ? text: "" ); - // auto height/width modes get handled here - textbox_moveresize(tb, tb->x, tb->y, tb->w, tb->h); + // auto height/width modes get handled here + textbox_moveresize( tb, tb->x, tb->y, tb->w, tb->h ); - // edit mode controls - if (tb->flags & TB_EDITABLE) - { - tb->xim = XOpenIM(display, NULL, NULL, NULL); - tb->xic = XCreateIC(tb->xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, tb->window, XNFocusWindow, tb->window, NULL); - } + // edit mode controls + if ( tb->flags & TB_EDITABLE ) { + tb->xim = XOpenIM( display, NULL, NULL, NULL ); + tb->xic = XCreateIC( tb->xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, tb->window, XNFocusWindow, tb->window, NULL ); + } - return tb; + return tb; } // set an Xft font by name -void textbox_font(textbox *tb, char *font, char *fg, char *bg) +void textbox_font( textbox *tb, char *font, char *fg, char *bg ) { - if (tb->font) XftFontClose(display, tb->font); - tb->font = XftFontOpenName(display, DefaultScreen(display), font); + if ( tb->font ) XftFontClose( display, tb->font ); - XftColorAllocName(display, DefaultVisual(display, DefaultScreen(display)), DefaultColormap(display, DefaultScreen(display)), fg, &tb->color_fg); - XftColorAllocName(display, DefaultVisual(display, DefaultScreen(display)), DefaultColormap(display, DefaultScreen(display)), bg, &tb->color_bg); + tb->font = XftFontOpenName( display, DefaultScreen( display ), font ); + + XftColorAllocName( display, DefaultVisual( display, DefaultScreen( display ) ), DefaultColormap( display, DefaultScreen( display ) ), fg, &tb->color_fg ); + XftColorAllocName( display, DefaultVisual( display, DefaultScreen( display ) ), DefaultColormap( display, DefaultScreen( display ) ), bg, &tb->color_bg ); } // outer code may need line height, width, etc -void textbox_extents(textbox *tb) +void textbox_extents( textbox *tb ) { - int length = strlen(tb->text) + strlen(tb->prompt); - char *line = alloca(length + 1); - sprintf(line, "%s%s", tb->prompt, tb->text); - XftTextExtents8(display, tb->font, (unsigned char*)line, length, &tb->extents); + int length = strlen( tb->text ) + strlen( tb->prompt ); + char *line = alloca( length + 1 ); + sprintf( line, "%s%s", tb->prompt, tb->text ); + XftTextExtents8( display, tb->font, ( unsigned char* )line, length, &tb->extents ); } // set the default text to display -void textbox_text(textbox *tb, char *text) +void textbox_text( textbox *tb, char *text ) { - if (tb->text) free(tb->text); - tb->text = strdup(text); - tb->cursor = MAX(0,MIN((int)strlen(text), tb->cursor)); - textbox_extents(tb); + if ( tb->text ) free( tb->text ); + + tb->text = strdup( text ); + tb->cursor = MAX( 0,MIN( ( int )strlen( text ), tb->cursor ) ); + textbox_extents( tb ); } // set an input prompt for edit mode -void textbox_prompt(textbox *tb, char *text) +void textbox_prompt( textbox *tb, char *text ) { - if (tb->prompt) free(tb->prompt); - tb->prompt = strdup(text); - textbox_extents(tb); + if ( tb->prompt ) free( tb->prompt ); + + tb->prompt = strdup( text ); + textbox_extents( tb ); } // within the parent. handled auto width/height modes -void textbox_moveresize(textbox *tb, int x, int y, int w, int h) +void textbox_moveresize( textbox *tb, int x, int y, int w, int h ) { - if (tb->flags & TB_AUTOHEIGHT) - h = tb->font->ascent + tb->font->descent; + if ( tb->flags & TB_AUTOHEIGHT ) + h = tb->font->ascent + tb->font->descent; - if (tb->flags & TB_AUTOWIDTH) - w = tb->extents.width; + if ( tb->flags & TB_AUTOWIDTH ) + w = tb->extents.width; - if (x != tb->x || y != tb->y || w != tb->w || h != tb->h) - { - tb->x = x; tb->y = y; tb->w = MAX(1, w); tb->h = MAX(1, h); - XMoveResizeWindow(display, tb->window, tb->x, tb->y, tb->w, tb->h); - } + if ( x != tb->x || y != tb->y || w != tb->w || h != tb->h ) { + tb->x = x; + tb->y = y; + tb->w = MAX( 1, w ); + tb->h = MAX( 1, h ); + XMoveResizeWindow( display, tb->window, tb->x, tb->y, tb->w, tb->h ); + } } -void textbox_show(textbox *tb) +void textbox_show( textbox *tb ) { - XMapWindow(display, tb->window); + XMapWindow( display, tb->window ); } -void textbox_hide(textbox *tb) +void textbox_hide( textbox *tb ) { - XUnmapWindow(display, tb->window); + XUnmapWindow( display, tb->window ); } // will also unmap the window if still displayed -void textbox_free(textbox *tb) +void textbox_free( textbox *tb ) { - if (tb->flags & TB_EDITABLE) - { - XDestroyIC(tb->xic); - XCloseIM(tb->xim); - } + if ( tb->flags & TB_EDITABLE ) { + XDestroyIC( tb->xic ); + XCloseIM( tb->xim ); + } - if (tb->text) free(tb->text); - if (tb->prompt) free(tb->prompt); - if (tb->font) XftFontClose(display, tb->font); + if ( tb->text ) free( tb->text ); - XDestroyWindow(display, tb->window); - free(tb); + if ( tb->prompt ) free( tb->prompt ); + + if ( tb->font ) XftFontClose( display, tb->font ); + + XDestroyWindow( display, tb->window ); + free( tb ); } -void textbox_draw(textbox *tb) +void textbox_draw( textbox *tb ) { - int i; - XGlyphInfo extents; + int i; + XGlyphInfo extents; - GC context = XCreateGC(display, tb->window, 0, 0); - Pixmap canvas = XCreatePixmap(display, tb->window, tb->w, tb->h, DefaultDepth(display, DefaultScreen(display))); - XftDraw *draw = XftDrawCreate(display, canvas, DefaultVisual(display, DefaultScreen(display)), DefaultColormap(display, DefaultScreen(display))); + GC context = XCreateGC( display, tb->window, 0, 0 ); + Pixmap canvas = XCreatePixmap( display, tb->window, tb->w, tb->h, DefaultDepth( display, DefaultScreen( display ) ) ); + XftDraw *draw = XftDrawCreate( display, canvas, DefaultVisual( display, DefaultScreen( display ) ), DefaultColormap( display, DefaultScreen( display ) ) ); - // clear canvas - XftDrawRect(draw, &tb->color_bg, 0, 0, tb->w, tb->h); + // clear canvas + XftDrawRect( draw, &tb->color_bg, 0, 0, tb->w, tb->h ); - char *line = tb->text, - *text = tb->text ? tb->text: "", - *prompt = tb->prompt ? tb->prompt: ""; + char *line = tb->text, + *text = tb->text ? tb->text: "", + *prompt = tb->prompt ? tb->prompt: ""; - int text_len = strlen(text); - int length = text_len; - int line_height = tb->font->ascent + tb->font->descent; - int line_width = 0; + int text_len = strlen( text ); + int length = text_len; + int line_height = tb->font->ascent + tb->font->descent; + int line_width = 0; - int cursor_x = 0; - int cursor_offset = 0; - int cursor_width = MAX(2, line_height/10); + int cursor_x = 0; + int cursor_offset = 0; + int cursor_width = MAX( 2, line_height/10 ); - if (tb->flags & TB_EDITABLE) - { - int prompt_len = strlen(prompt); - length = text_len + prompt_len; - cursor_offset = MIN(tb->cursor + prompt_len, length); + if ( tb->flags & TB_EDITABLE ) { + int prompt_len = strlen( prompt ); + length = text_len + prompt_len; + cursor_offset = MIN( tb->cursor + prompt_len, length ); - line = alloca(length + 10); - sprintf(line, "%s%s", prompt, text); + line = alloca( length + 10 ); + sprintf( line, "%s%s", prompt, text ); - // replace spaces so XftTextExtents8 includes their width - for (i = 0; i < length; i++) if (isspace(line[i])) line[i] = '_'; + // replace spaces so XftTextExtents8 includes their width + for ( i = 0; i < length; i++ ) if ( isspace( line[i] ) ) line[i] = '_'; - // calc cursor position - XftTextExtents8(display, tb->font, (unsigned char*)line, cursor_offset, &extents); - cursor_x = extents.width; + // calc cursor position + XftTextExtents8( display, tb->font, ( unsigned char* )line, cursor_offset, &extents ); + cursor_x = extents.width; - // restore correct text string with spaces - sprintf(line, "%s%s", prompt, text); - } + // restore correct text string with spaces + sprintf( line, "%s%s", prompt, text ); + } - // calc full input text width - XftTextExtents8(display, tb->font, (unsigned char*)line, length, &extents); - line_width = extents.width; + // calc full input text width + XftTextExtents8( display, tb->font, ( unsigned char* )line, length, &extents ); + line_width = extents.width; - int x = 2, y = tb->font->ascent; - if (tb->flags & TB_RIGHT) x = tb->w - line_width; - if (tb->flags & TB_CENTER) x = (tb->w - line_width) / 2; + int x = 2, y = tb->font->ascent; - // draw the text, including any prompt in edit mode - XftDrawString8(draw, &tb->color_fg, tb->font, x, y, (unsigned char*)line, length); + if ( tb->flags & TB_RIGHT ) x = tb->w - line_width; - // draw the cursor - if (tb->flags & TB_EDITABLE) - XftDrawRect(draw, &tb->color_fg, cursor_x, 2, cursor_width, line_height-4); + if ( tb->flags & TB_CENTER ) x = ( tb->w - line_width ) / 2; - // flip canvas to window - XCopyArea(display, canvas, tb->window, context, 0, 0, tb->w, tb->h, 0, 0); + // draw the text, including any prompt in edit mode + XftDrawString8( draw, &tb->color_fg, tb->font, x, y, ( unsigned char* )line, length ); - XFreeGC(display, context); - XftDrawDestroy(draw); - XFreePixmap(display, canvas); + // draw the cursor + if ( tb->flags & TB_EDITABLE ) + XftDrawRect( draw, &tb->color_fg, cursor_x, 2, cursor_width, line_height-4 ); + + // flip canvas to window + XCopyArea( display, canvas, tb->window, context, 0, 0, tb->w, tb->h, 0, 0 ); + + XFreeGC( display, context ); + XftDrawDestroy( draw ); + XFreePixmap( display, canvas ); } // cursor handling for edit mode -void textbox_cursor(textbox *tb, int pos) +void textbox_cursor( textbox *tb, int pos ) { - tb->cursor = MAX(0, MIN((int)strlen(tb->text), pos)); + tb->cursor = MAX( 0, MIN( ( int )strlen( tb->text ), pos ) ); } // move right -void textbox_cursor_inc(textbox *tb) +void textbox_cursor_inc( textbox *tb ) { - textbox_cursor(tb, tb->cursor+1); + textbox_cursor( tb, tb->cursor+1 ); } // move left -void textbox_cursor_dec(textbox *tb) +void textbox_cursor_dec( textbox *tb ) { - textbox_cursor(tb, tb->cursor-1); + textbox_cursor( tb, tb->cursor-1 ); } // beginning of line -void textbox_cursor_home(textbox *tb) +void textbox_cursor_home( textbox *tb ) { - tb->cursor = 0; + tb->cursor = 0; } // end of line -void textbox_cursor_end(textbox *tb) +void textbox_cursor_end( textbox *tb ) { - tb->cursor = (int)strlen(tb->text); + tb->cursor = ( int )strlen( tb->text ); } // insert text -void textbox_insert(textbox *tb, int pos, char *str) +void textbox_insert( textbox *tb, int pos, char *str ) { - int len = (int)strlen(tb->text), slen = (int)strlen(str); - pos = MAX(0, MIN(len, pos)); - // expand buffer - tb->text = realloc(tb->text, len + slen + 1); - // move everything after cursor upward - char *at = tb->text + pos; - memmove(at + slen, at, len - pos + 1); - // insert new str - memmove(at, str, slen); - textbox_extents(tb); + int len = ( int )strlen( tb->text ), slen = ( int )strlen( str ); + pos = MAX( 0, MIN( len, pos ) ); + // expand buffer + tb->text = realloc( tb->text, len + slen + 1 ); + // move everything after cursor upward + char *at = tb->text + pos; + memmove( at + slen, at, len - pos + 1 ); + // insert new str + memmove( at, str, slen ); + textbox_extents( tb ); } // remove text -void textbox_delete(textbox *tb, int pos, int dlen) +void textbox_delete( textbox *tb, int pos, int dlen ) { - int len = strlen(tb->text); - pos = MAX(0, MIN(len, pos)); - // move everything after pos+dlen down - char *at = tb->text + pos; - memmove(at, at + dlen, len - pos); - textbox_extents(tb); + int len = strlen( tb->text ); + pos = MAX( 0, MIN( len, pos ) ); + // move everything after pos+dlen down + char *at = tb->text + pos; + memmove( at, at + dlen, len - pos ); + textbox_extents( tb ); } // insert one character -void textbox_cursor_ins(textbox *tb, char c) +void textbox_cursor_ins( textbox *tb, char c ) { - char tmp[2] = { c, 0 }; - textbox_insert(tb, tb->cursor, tmp); - textbox_cursor_inc(tb); + char tmp[2] = { c, 0 }; + textbox_insert( tb, tb->cursor, tmp ); + textbox_cursor_inc( tb ); } // delete on character -void textbox_cursor_del(textbox *tb) +void textbox_cursor_del( textbox *tb ) { - textbox_delete(tb, tb->cursor, 1); + textbox_delete( tb, tb->cursor, 1 ); } // back up and delete one character -void textbox_cursor_bkspc(textbox *tb) +void textbox_cursor_bkspc( textbox *tb ) { - if (tb->cursor > 0) - { - textbox_cursor_dec(tb); - textbox_cursor_del(tb); - } + if ( tb->cursor > 0 ) { + textbox_cursor_dec( tb ); + textbox_cursor_del( tb ); + } } // handle a keypress in edit mode // 0 = unhandled // 1 = handled // -1 = handled and return pressed (finished) -int textbox_keypress(textbox *tb, XEvent *ev) +int textbox_keypress( textbox *tb, XEvent *ev ) { - KeySym key; Status stat; - char pad[32]; int len; + KeySym key; + Status stat; + char pad[32]; + int len; - if (!(tb->flags & TB_EDITABLE)) return 0; + if ( !( tb->flags & TB_EDITABLE ) ) return 0; - len = XmbLookupString(tb->xic, &ev->xkey, pad, sizeof(pad), &key, &stat); - pad[len] = 0; + len = XmbLookupString( tb->xic, &ev->xkey, pad, sizeof( pad ), &key, &stat ); + pad[len] = 0; - if (key == XK_Left) - { - textbox_cursor_dec(tb); - return 1; - } - else - if (key == XK_Right) - { - textbox_cursor_inc(tb); - return 1; - } - else - if (key == XK_Home) - { - textbox_cursor_home(tb); - return 1; - } - else - if (key == XK_End) - { - textbox_cursor_end(tb); - return 1; - } - else - if (key == XK_Delete) - { - textbox_cursor_del(tb); - return 1; - } - else - if (key == XK_BackSpace) - { - textbox_cursor_bkspc(tb); - return 1; - } - else - if (key == XK_Return) - { - return -1; - } - else - if (!iscntrl(*pad)) - { - textbox_insert(tb, tb->cursor, pad); - textbox_cursor_inc(tb); - return 1; - } - return 0; + if ( key == XK_Left ) { + textbox_cursor_dec( tb ); + return 1; + } else if ( key == XK_Right ) { + textbox_cursor_inc( tb ); + return 1; + } else if ( key == XK_Home ) { + textbox_cursor_home( tb ); + return 1; + } else if ( key == XK_End ) { + textbox_cursor_end( tb ); + return 1; + } else if ( key == XK_Delete ) { + textbox_cursor_del( tb ); + return 1; + } else if ( key == XK_BackSpace ) { + textbox_cursor_bkspc( tb ); + return 1; + } else if ( key == XK_Return ) { + return -1; + } else if ( !iscntrl( *pad ) ) { + textbox_insert( tb, tb->cursor, pad ); + textbox_cursor_inc( tb ); + return 1; + } + + return 0; }