2008-07-15 18:53:38 +02:00
/* See LICENSE file for copyright and license details.
*
2008-07-16 08:59:28 +02:00
* To understand svkbd , start reading main ( ) .
2008-07-15 18:53:38 +02:00
*/
# include <locale.h>
# include <stdarg.h>
# include <stdio.h>
# include <string.h>
2008-07-15 22:12:55 +02:00
# include <stdlib.h>
2008-07-15 18:53:38 +02:00
# include <X11/keysym.h>
2020-08-02 15:46:14 +02:00
# include <X11/keysymdef.h>
# include <X11/XF86keysym.h>
2008-07-15 18:53:38 +02:00
# include <X11/Xatom.h>
# include <X11/Xlib.h>
# include <X11/Xutil.h>
2008-07-16 08:59:28 +02:00
# include <X11/Xproto.h>
2008-07-15 22:12:55 +02:00
# include <X11/extensions/XTest.h>
2020-08-02 15:46:12 +02:00
# include <X11/Xft/Xft.h>
# ifdef XINERAMA
# include <X11/extensions/Xinerama.h>
# endif
2020-08-02 15:46:07 +02:00
# include <signal.h>
2020-08-02 15:46:14 +02:00
# include <time.h>
# include <unistd.h>
2020-08-02 15:46:09 +02:00
# include <sys/select.h>
2020-08-02 15:46:14 +02:00
# include <sys/time.h>
2020-08-02 15:46:09 +02:00
2020-08-02 15:46:12 +02:00
# include "drw.h"
# include "util.h"
2008-07-15 18:53:38 +02:00
/* macros */
# define LENGTH(x) (sizeof x / sizeof x[0])
2020-08-02 15:46:12 +02:00
# define TEXTW(X) (drw_fontset_getwidth(drw, (X)))
2020-08-02 15:46:14 +02:00
# define STRINGTOKEYSYM(X) (XStringToKeySym(X))
2008-07-15 18:53:38 +02:00
/* enums */
2020-08-02 15:46:12 +02:00
enum { SchemeNorm , SchemePress , SchemeHighlight , SchemeLast } ;
2011-04-06 09:03:25 +02:00
enum { NetWMWindowType , NetLast } ;
2008-07-15 18:53:38 +02:00
/* typedefs */
typedef unsigned int uint ;
typedef unsigned long ulong ;
typedef struct {
2008-07-15 22:12:55 +02:00
char * label ;
2008-07-15 18:53:38 +02:00
KeySym keysym ;
2008-07-15 22:12:55 +02:00
uint width ;
2008-07-15 18:53:38 +02:00
int x , y , w , h ;
2008-07-15 22:12:55 +02:00
Bool pressed ;
2011-10-09 17:22:50 +02:00
Bool highlighted ;
2008-07-15 18:53:38 +02:00
} Key ;
2008-07-16 08:59:28 +02:00
typedef struct {
KeySym mod ;
uint button ;
} Buttonmod ;
2008-07-15 18:53:38 +02:00
/* function declarations */
2011-10-09 17:22:50 +02:00
static void motionnotify ( XEvent * e ) ;
2008-07-15 18:53:38 +02:00
static void buttonpress ( XEvent * e ) ;
2008-07-15 22:12:55 +02:00
static void buttonrelease ( XEvent * e ) ;
2008-07-15 18:53:38 +02:00
static void cleanup ( void ) ;
static void configurenotify ( XEvent * e ) ;
2011-03-25 15:14:12 +01:00
static void countrows ( ) ;
2020-08-02 15:46:16 +02:00
static int countkeys ( Key * k ) ;
2008-07-15 18:53:38 +02:00
static void drawkeyboard ( void ) ;
2008-07-15 22:12:55 +02:00
static void drawkey ( Key * k ) ;
2008-07-15 18:53:38 +02:00
static void expose ( XEvent * e ) ;
2008-07-15 22:12:55 +02:00
static Key * findkey ( int x , int y ) ;
2008-07-15 18:53:38 +02:00
static void leavenotify ( XEvent * e ) ;
2008-07-16 08:59:28 +02:00
static void press ( Key * k , KeySym mod ) ;
2020-08-02 15:46:14 +02:00
static double get_press_duration ( ) ;
2008-07-15 18:53:38 +02:00
static void run ( void ) ;
static void setup ( void ) ;
2020-08-02 15:46:14 +02:00
static void simulate_keypress ( KeySym keysym ) ;
static void simulate_keyrelease ( KeySym keysym ) ;
static void showoverlay ( int idx ) ;
static void hideoverlay ( ) ;
static void cyclelayer ( ) ;
2020-08-02 15:46:16 +02:00
static void setlayer ( ) ;
2020-08-02 15:46:15 +02:00
static void togglelayer ( ) ;
2011-10-09 17:22:50 +02:00
static void unpress ( Key * k , KeySym mod ) ;
2008-07-15 22:12:55 +02:00
static void updatekeys ( ) ;
2008-07-15 18:53:38 +02:00
/* variables */
static int screen ;
static void ( * handler [ LASTEvent ] ) ( XEvent * ) = {
[ ButtonPress ] = buttonpress ,
2008-07-15 22:12:55 +02:00
[ ButtonRelease ] = buttonrelease ,
2008-07-15 18:53:38 +02:00
[ ConfigureNotify ] = configurenotify ,
[ Expose ] = expose ,
[ LeaveNotify ] = leavenotify ,
2011-10-09 17:22:50 +02:00
[ MotionNotify ] = motionnotify
2008-07-15 18:53:38 +02:00
} ;
2011-03-25 15:14:12 +01:00
static Atom netatom [ NetLast ] ;
2008-07-15 18:53:38 +02:00
static Display * dpy ;
2020-08-02 15:46:12 +02:00
static Drw * drw ;
2008-07-15 18:53:38 +02:00
static Window root , win ;
2020-08-02 15:46:12 +02:00
static Clr * scheme [ SchemeLast ] ;
2011-10-09 17:45:12 +02:00
static Bool running = True , isdock = False ;
2008-07-16 08:59:28 +02:00
static KeySym pressedmod = 0 ;
2020-08-02 15:46:14 +02:00
static struct timeval pressbegin ;
static int currentlayer = 0 ;
2020-08-02 15:46:15 +02:00
static int enableoverlays = 1 ;
2020-08-02 15:46:14 +02:00
static int currentoverlay = - 1 ; // -1 = no overlay
static KeySym overlaykeysym = 0 ; //keysym for which the overlay is presented
static int releaseprotect = 0 ; //set to 1 after overlay is shown, protecting against immediate release
static int tmp_keycode = 1 ;
2011-04-04 13:47:27 +02:00
static int rows = 0 , ww = 0 , wh = 0 , wx = 0 , wy = 0 ;
2011-03-25 15:14:12 +01:00
static char * name = " svkbd " ;
2020-08-02 15:46:14 +02:00
static int debug = 0 ;
2020-08-02 15:46:15 +02:00
static int numlayers = 0 ;
2020-08-02 15:46:16 +02:00
static int numkeys = 0 ;
2020-08-02 15:46:14 +02:00
static KeySym ispressingkeysym ;
2011-10-09 17:22:50 +02:00
Bool ispressing = False ;
2020-08-02 15:46:09 +02:00
Bool sigtermd = False ;
2011-10-09 17:22:50 +02:00
2008-07-15 18:53:38 +02:00
/* configuration, allows nested code to access above variables */
# include "config.h"
2020-05-29 14:24:17 +02:00
# ifndef LAYOUT
# error "make sure to define LAYOUT"
# endif
# include LAYOUT
2008-07-15 18:53:38 +02:00
2020-08-02 15:46:19 +02:00
static Key keys [ KEYS ] = { NULL } ;
2020-08-02 15:46:15 +02:00
static Key * layers [ LAYERS ] ;
2011-10-09 17:22:50 +02:00
void
motionnotify ( XEvent * e )
{
XPointerMovedEvent * ev = & e - > xmotion ;
int i ;
2020-08-02 15:46:16 +02:00
for ( i = 0 ; i < numkeys ; i + + ) {
2011-10-09 17:22:50 +02:00
if ( keys [ i ] . keysym & & ev - > x > keys [ i ] . x
& & ev - > x < keys [ i ] . x + keys [ i ] . w
& & ev - > y > keys [ i ] . y
& & ev - > y < keys [ i ] . y + keys [ i ] . h ) {
if ( keys [ i ] . highlighted ! = True ) {
if ( ispressing ) {
keys [ i ] . pressed = True ;
} else {
keys [ i ] . highlighted = True ;
}
drawkey ( & keys [ i ] ) ;
}
continue ;
}
if ( ! IsModifierKey ( keys [ i ] . keysym ) & & keys [ i ] . pressed = = True ) {
2012-10-26 23:53:29 +02:00
unpress ( & keys [ i ] , 0 ) ;
2011-10-09 17:22:50 +02:00
drawkey ( & keys [ i ] ) ;
}
if ( keys [ i ] . highlighted = = True ) {
keys [ i ] . highlighted = False ;
drawkey ( & keys [ i ] ) ;
}
}
}
2008-07-15 18:53:38 +02:00
void
buttonpress ( XEvent * e ) {
2008-07-16 08:59:28 +02:00
int i ;
2008-07-15 22:12:55 +02:00
XButtonPressedEvent * ev = & e - > xbutton ;
Key * k ;
2008-07-16 08:59:28 +02:00
KeySym mod = 0 ;
2008-07-15 22:12:55 +02:00
2011-10-09 17:22:50 +02:00
ispressing = True ;
for ( i = 0 ; i < LENGTH ( buttonmods ) ; i + + ) {
2008-07-16 08:59:28 +02:00
if ( ev - > button = = buttonmods [ i ] . button ) {
mod = buttonmods [ i ] . mod ;
break ;
2008-07-15 22:12:55 +02:00
}
2011-10-09 17:22:50 +02:00
}
2008-07-16 08:59:28 +02:00
if ( ( k = findkey ( ev - > x , ev - > y ) ) )
press ( k , mod ) ;
2008-07-15 22:12:55 +02:00
}
void
buttonrelease ( XEvent * e ) {
2011-10-09 17:22:50 +02:00
int i ;
2008-07-15 22:12:55 +02:00
XButtonPressedEvent * ev = & e - > xbutton ;
2008-07-16 08:59:28 +02:00
Key * k ;
2011-10-09 17:22:50 +02:00
KeySym mod = 0 ;
ispressing = False ;
2008-07-15 22:12:55 +02:00
2011-10-09 17:22:50 +02:00
for ( i = 0 ; i < LENGTH ( buttonmods ) ; i + + ) {
if ( ev - > button = = buttonmods [ i ] . button ) {
mod = buttonmods [ i ] . mod ;
break ;
}
}
2012-10-26 23:53:29 +02:00
if ( ev - > x < 0 | | ev - > y < 0 ) {
unpress ( NULL , mod ) ;
} else {
if ( ( k = findkey ( ev - > x , ev - > y ) ) )
unpress ( k , mod ) ;
}
2008-07-15 18:53:38 +02:00
}
void
cleanup ( void ) {
2020-08-02 15:46:09 +02:00
int i ;
2020-08-02 15:46:12 +02:00
for ( i = 0 ; i < SchemeLast ; i + + )
free ( scheme [ i ] ) ;
drw_sync ( drw ) ;
2020-08-02 15:46:15 +02:00
drw_free ( drw ) ;
2020-08-02 15:46:12 +02:00
XSync ( dpy , False ) ;
2020-08-02 15:46:13 +02:00
drw_free ( drw ) ;
2008-07-15 18:53:38 +02:00
XDestroyWindow ( dpy , win ) ;
XSync ( dpy , False ) ;
XSetInputFocus ( dpy , PointerRoot , RevertToPointerRoot , CurrentTime ) ;
}
void
configurenotify ( XEvent * e ) {
XConfigureEvent * ev = & e - > xconfigure ;
if ( ev - > window = = win & & ( ev - > width ! = ww | | ev - > height ! = wh ) ) {
ww = ev - > width ;
wh = ev - > height ;
2020-08-02 15:46:12 +02:00
drw_resize ( drw , ww , wh ) ;
2008-07-15 22:12:55 +02:00
updatekeys ( ) ;
2008-07-15 18:53:38 +02:00
}
}
2011-03-25 15:14:12 +01:00
void
countrows ( ) {
int i = 0 ;
2020-08-02 15:46:16 +02:00
for ( i = 0 , rows = 1 ; i < numkeys ; i + + ) {
2011-03-25 15:14:12 +01:00
if ( keys [ i ] . keysym = = 0 )
rows + + ;
2011-10-09 17:22:50 +02:00
}
2011-03-25 15:14:12 +01:00
}
2020-08-02 15:46:16 +02:00
int
countkeys ( Key * layer ) {
int keys = 0 ;
int i ;
for ( i = 0 ; i < KEYS ; i + + ) {
if ( i > 0 & & layer [ i ] . keysym = = 0 & & layer [ i - 1 ] . keysym = = 0 ) {
keys - - ;
break ;
}
keys + + ;
}
return keys ;
}
2008-07-15 18:53:38 +02:00
void
drawkeyboard ( void ) {
2008-07-15 22:12:55 +02:00
int i ;
2008-07-15 18:53:38 +02:00
2020-08-02 15:46:16 +02:00
for ( i = 0 ; i < numkeys ; i + + ) {
2008-07-15 18:53:38 +02:00
if ( keys [ i ] . keysym ! = 0 )
2008-07-15 22:12:55 +02:00
drawkey ( & keys [ i ] ) ;
2008-07-15 18:53:38 +02:00
}
}
void
2008-07-15 22:12:55 +02:00
drawkey ( Key * k ) {
2020-08-02 15:46:12 +02:00
int x , y , w , h ;
2008-07-15 22:12:55 +02:00
const char * l ;
2008-07-15 18:53:38 +02:00
2008-07-15 22:12:55 +02:00
if ( k - > pressed )
2020-08-02 15:46:12 +02:00
drw_setscheme ( drw , scheme [ SchemePress ] ) ;
2011-10-09 17:22:50 +02:00
else if ( k - > highlighted )
2020-08-02 15:46:12 +02:00
drw_setscheme ( drw , scheme [ SchemeHighlight ] ) ;
2008-07-15 22:12:55 +02:00
else
2020-08-02 15:46:12 +02:00
drw_setscheme ( drw , scheme [ SchemeNorm ] ) ;
drw_rect ( drw , k - > x , k - > y , k - > w , k - > h , 1 , 1 ) ;
drw_rect ( drw , k - > x , k - > y , k - > w , k - > h , 0 , 0 ) ;
2020-08-02 15:46:15 +02:00
if ( k - > keysym = = XK_KP_Insert ) {
if ( enableoverlays ) {
l = " ≅ " ;
} else {
l = " ≇ " ;
}
} else if ( k - > label ) {
2008-07-15 22:12:55 +02:00
l = k - > label ;
2011-10-09 17:22:50 +02:00
} else {
2008-07-15 22:12:55 +02:00
l = XKeysymToString ( k - > keysym ) ;
2011-10-09 17:22:50 +02:00
}
2020-08-02 15:46:12 +02:00
h = fontsize * 2 ;
y = k - > y + ( k - > h / 2 ) - ( h / 2 ) ;
w = TEXTW ( l ) ;
x = k - > x + ( k - > w / 2 ) - ( w / 2 ) ;
drw_text ( drw , x , y , w , h , 0 , l , 0 ) ;
drw_map ( drw , win , k - > x , k - > y , k - > w , k - > h ) ;
2008-07-15 18:53:38 +02:00
}
void
expose ( XEvent * e ) {
XExposeEvent * ev = & e - > xexpose ;
if ( ev - > count = = 0 & & ( ev - > window = = win ) )
drawkeyboard ( ) ;
}
2008-07-15 22:12:55 +02:00
Key *
findkey ( int x , int y ) {
int i ;
2011-03-24 18:14:07 +01:00
2020-08-02 15:46:16 +02:00
for ( i = 0 ; i < numkeys ; i + + ) {
2008-07-15 22:12:55 +02:00
if ( keys [ i ] . keysym & & x > keys [ i ] . x & &
x < keys [ i ] . x + keys [ i ] . w & &
2011-10-09 17:22:50 +02:00
y > keys [ i ] . y & & y < keys [ i ] . y + keys [ i ] . h ) {
2008-07-15 22:12:55 +02:00
return & keys [ i ] ;
2011-10-09 17:22:50 +02:00
}
}
2008-07-15 22:12:55 +02:00
return NULL ;
}
2008-07-15 18:53:38 +02:00
2020-08-02 15:46:14 +02:00
int
hasoverlay ( KeySym keysym ) {
int begin , i ;
begin = 0 ;
for ( i = 0 ; i < OVERLAYS ; i + + ) {
if ( overlay [ i ] . keysym = = XK_Cancel ) {
begin = i + 1 ;
} else if ( overlay [ i ] . keysym = = keysym ) {
return begin + 1 ;
}
}
return - 1 ;
}
2008-07-15 18:53:38 +02:00
void
leavenotify ( XEvent * e ) {
2020-08-02 15:46:14 +02:00
if ( currentoverlay ! = - 1 ) {
hideoverlay ( ) ;
}
2011-10-09 17:22:50 +02:00
unpress ( NULL , 0 ) ;
2008-07-15 18:53:38 +02:00
}
2020-08-02 15:46:14 +02:00
void record_press_begin ( KeySym ks ) {
//record the begin of the press, don't simulate the actual keypress yet
gettimeofday ( & pressbegin , NULL ) ;
ispressingkeysym = ks ;
}
2008-07-16 08:59:28 +02:00
void
press ( Key * k , KeySym mod ) {
int i ;
2020-08-02 15:46:14 +02:00
int overlayidx = - 1 ;
2008-07-16 08:59:28 +02:00
k - > pressed = ! k - > pressed ;
2020-08-02 15:46:14 +02:00
if ( debug ) { printf ( " Begin press: %ld \n " , k - > keysym ) ; fflush ( stdout ) ; }
pressbegin . tv_sec = 0 ;
pressbegin . tv_usec = 0 ;
ispressingkeysym = 0 ;
2020-08-02 15:46:15 +02:00
if ( ! IsModifierKey ( k - > keysym ) ) {
if ( enableoverlays & & currentoverlay = = - 1 )
2020-08-02 15:46:14 +02:00
overlayidx = hasoverlay ( k - > keysym ) ;
2020-08-02 15:46:15 +02:00
if ( enableoverlays & & overlayidx ! = - 1 ) {
2020-08-02 15:46:14 +02:00
if ( ! pressbegin . tv_sec & & ! pressbegin . tv_usec ) {
//record the begin of the press, don't simulate the actual keypress yet
record_press_begin ( k - > keysym ) ;
}
} else {
if ( debug ) { printf ( " Simulating press: %ld \n " , k - > keysym ) ; fflush ( stdout ) ; }
2020-08-02 15:46:16 +02:00
for ( i = 0 ; i < numkeys ; i + + ) {
2020-08-02 15:46:14 +02:00
if ( keys [ i ] . pressed & & IsModifierKey ( keys [ i ] . keysym ) ) {
simulate_keypress ( keys [ i ] . keysym ) ;
}
}
pressedmod = mod ;
if ( pressedmod ) {
simulate_keypress ( mod ) ;
}
simulate_keypress ( k - > keysym ) ;
2012-10-27 00:01:15 +02:00
2020-08-02 15:46:16 +02:00
for ( i = 0 ; i < numkeys ; i + + ) {
2020-08-02 15:46:14 +02:00
if ( keys [ i ] . pressed & & IsModifierKey ( keys [ i ] . keysym ) ) {
simulate_keyrelease ( keys [ i ] . keysym ) ;
}
2012-10-27 00:01:15 +02:00
}
}
2008-07-16 08:59:28 +02:00
}
drawkey ( k ) ;
}
2020-08-02 15:46:14 +02:00
int tmp_remap ( KeySym keysym ) {
XChangeKeyboardMapping ( dpy , tmp_keycode , 1 , & keysym , 1 ) ;
XSync ( dpy , False ) ;
return tmp_keycode ;
}
void
simulate_keypress ( KeySym keysym ) {
KeyCode code = XKeysymToKeycode ( dpy , keysym ) ;
if ( code = = 0 )
code = tmp_remap ( keysym ) ;
XTestFakeKeyEvent ( dpy , code , True , 0 ) ;
}
void
simulate_keyrelease ( KeySym keysym ) {
KeyCode code = XKeysymToKeycode ( dpy , keysym ) ;
if ( code = = 0 )
code = tmp_remap ( keysym ) ;
XTestFakeKeyEvent ( dpy , code , False , 0 ) ;
}
double get_press_duration ( ) {
struct timeval now ;
gettimeofday ( & now , NULL ) ;
return ( double ) ( ( now . tv_sec * 1000000L + now . tv_usec ) - ( pressbegin . tv_sec * 1000000L + pressbegin . tv_usec ) ) / ( double ) 1000000L ;
}
2011-10-09 17:22:50 +02:00
void
unpress ( Key * k , KeySym mod ) {
int i ;
if ( k ! = NULL ) {
switch ( k - > keysym ) {
case XK_Cancel :
2020-08-02 15:46:14 +02:00
cyclelayer ( ) ;
2020-08-02 15:46:07 +02:00
break ;
2020-08-02 15:46:15 +02:00
case XK_script_switch :
togglelayer ( ) ;
break ;
case XK_KP_Insert :
enableoverlays = ! enableoverlays ;
break ;
2020-08-02 15:46:07 +02:00
case XK_Break :
running = False ;
2011-10-09 17:22:50 +02:00
default :
break ;
}
}
2020-08-02 15:46:14 +02:00
2020-08-02 15:46:15 +02:00
if ( ( pressbegin . tv_sec | | pressbegin . tv_usec ) & & enableoverlays & & k & & k - > keysym = = ispressingkeysym ) {
2020-08-02 15:46:14 +02:00
if ( currentoverlay = = - 1 ) {
if ( get_press_duration ( ) < overlay_delay ) {
if ( debug ) { printf ( " Delayed simulation of press after release: %ld \n " , k - > keysym ) ; fflush ( stdout ) ; }
//simulate the press event, as we postponed it earlier in press()
2020-08-02 15:46:16 +02:00
for ( i = 0 ; i < numkeys ; i + + ) {
2020-08-02 15:46:14 +02:00
if ( keys [ i ] . pressed & & IsModifierKey ( keys [ i ] . keysym ) ) {
simulate_keypress ( keys [ i ] . keysym ) ;
}
}
pressedmod = mod ;
if ( pressedmod ) {
simulate_keypress ( mod ) ;
}
simulate_keypress ( k - > keysym ) ;
pressbegin . tv_sec = 0 ;
pressbegin . tv_usec = 0 ;
} else {
return ;
}
}
}
if ( debug ) {
if ( k ) {
printf ( " Simulation of release: %ld \n " , k - > keysym ) ; fflush ( stdout ) ;
} else {
2020-08-02 15:46:15 +02:00
printf ( " Simulation of release (all keys) \n " ) ; fflush ( stdout ) ;
2020-08-02 15:46:14 +02:00
}
}
2020-08-02 15:46:16 +02:00
for ( i = 0 ; i < numkeys ; i + + ) {
2011-10-09 17:22:50 +02:00
if ( keys [ i ] . pressed & & ! IsModifierKey ( keys [ i ] . keysym ) ) {
2020-08-02 15:46:14 +02:00
simulate_keyrelease ( keys [ i ] . keysym ) ;
2011-10-09 17:22:50 +02:00
keys [ i ] . pressed = 0 ;
drawkey ( & keys [ i ] ) ;
break ;
}
}
2020-08-02 15:46:16 +02:00
if ( i ! = numkeys ) {
2012-10-26 23:53:29 +02:00
if ( pressedmod ) {
2020-08-02 15:46:14 +02:00
simulate_keyrelease ( mod ) ;
2012-10-26 23:53:29 +02:00
}
pressedmod = 0 ;
2020-08-02 15:46:16 +02:00
for ( i = 0 ; i < numkeys ; i + + ) {
2011-10-09 17:22:50 +02:00
if ( keys [ i ] . pressed ) {
2020-08-02 15:46:14 +02:00
simulate_keyrelease ( keys [ i ] . keysym ) ;
2011-10-09 17:22:50 +02:00
keys [ i ] . pressed = 0 ;
drawkey ( & keys [ i ] ) ;
}
}
}
2020-08-02 15:46:14 +02:00
2020-08-02 15:46:15 +02:00
if ( enableoverlays & & currentoverlay ! = - 1 ) {
2020-08-02 15:46:14 +02:00
if ( releaseprotect ) {
releaseprotect = 0 ;
} else {
hideoverlay ( ) ;
}
}
2011-10-09 17:22:50 +02:00
}
2008-07-15 18:53:38 +02:00
void
run ( void ) {
XEvent ev ;
2020-08-02 15:46:09 +02:00
int xfd ;
fd_set fds ;
struct timeval tv ;
2020-08-02 15:46:14 +02:00
double duration = 0.0 ;
2020-08-02 15:46:18 +02:00
int i , r ;
2020-08-02 15:46:09 +02:00
2020-08-02 15:46:10 +02:00
2020-08-02 15:46:09 +02:00
xfd = ConnectionNumber ( dpy ) ;
tv . tv_usec = 0 ;
2020-08-02 15:46:14 +02:00
tv . tv_sec = 1 ;
2020-08-02 15:46:10 +02:00
//XSync(dpy, False);
XFlush ( dpy ) ;
2020-08-02 15:46:09 +02:00
while ( running ) {
2020-08-02 15:46:15 +02:00
usleep ( 100000L ) ;
2020-08-02 15:46:10 +02:00
FD_ZERO ( & fds ) ;
FD_SET ( xfd , & fds ) ;
2020-08-02 15:46:18 +02:00
r = select ( xfd + 1 , & fds , NULL , NULL , & tv ) ;
if ( r ) {
2020-08-02 15:46:10 +02:00
while ( XPending ( dpy ) ) {
XNextEvent ( dpy , & ev ) ;
if ( handler [ ev . type ] ) {
( handler [ ev . type ] ) ( & ev ) ; /* call handler */
}
}
2020-08-02 15:46:14 +02:00
} else {
2020-08-02 15:46:18 +02:00
//time-out expired without anything interesting happening, check for long-presses
2020-08-02 15:46:14 +02:00
if ( ispressing & & ispressingkeysym ) {
duration = get_press_duration ( ) ;
if ( debug = = 2 ) { printf ( " %f \n " , duration ) ; fflush ( stdout ) ; }
if ( get_press_duration ( ) > = overlay_delay ) {
if ( debug ) { printf ( " press duration %f \n " , duration ) ; fflush ( stdout ) ; }
2020-08-02 15:46:15 +02:00
showoverlay ( hasoverlay ( ispressingkeysym ) ) ;
2020-08-02 15:46:14 +02:00
pressbegin . tv_sec = 0 ;
pressbegin . tv_usec = 0 ;
ispressingkeysym = 0 ;
}
}
2020-08-02 15:46:09 +02:00
}
2020-08-02 15:46:18 +02:00
if ( r = = - 1 | | sigtermd ) {
// an error occurred or we received a signal
// E.g. Generally in scripts we want to call SIGTERM on svkbd in which case
// if the user is holding for example the enter key (to execute
// the kill or script that does the kill), that causes an issue
// since then X doesn't know the keyup is never coming.. (since
// process will be dead before finger lifts - in that case we
// just trigger out fake up presses for all keys
if ( debug ) { printf ( " signal received, releasing all keys " ) ; fflush ( stdout ) ; }
for ( i = 0 ; i < numkeys ; i + + ) {
XTestFakeKeyEvent ( dpy , XKeysymToKeycode ( dpy , keys [ i ] . keysym ) , False , 0 ) ;
}
running = False ;
}
2008-07-15 18:53:38 +02:00
}
}
void
setup ( void ) {
2011-02-01 20:04:02 +01:00
XSetWindowAttributes wa ;
2011-03-25 15:14:12 +01:00
XTextProperty str ;
2011-10-09 17:45:12 +02:00
XSizeHints * sizeh = NULL ;
2011-03-25 15:14:12 +01:00
XClassHint * ch ;
2011-10-09 17:45:12 +02:00
Atom atype = - 1 ;
2020-08-02 15:46:12 +02:00
int i , j , sh , sw ;
2011-03-25 15:14:12 +01:00
XWMHints * wmh ;
2008-07-15 22:12:55 +02:00
2020-08-02 15:46:12 +02:00
# if XINERAMA
XineramaScreenInfo * info = NULL ;
# endif
2008-07-15 18:53:38 +02:00
/* init screen */
screen = DefaultScreen ( dpy ) ;
root = RootWindow ( dpy , screen ) ;
2020-08-02 15:46:12 +02:00
# if XINERAMA
if ( XineramaIsActive ( dpy ) ) {
info = XineramaQueryScreens ( dpy , & i ) ;
sw = info [ 0 ] . width ;
sh = info [ 0 ] . height ;
XFree ( info ) ;
} else
# endif
{
sw = DisplayWidth ( dpy , screen ) ;
sh = DisplayHeight ( dpy , screen ) ;
}
2020-08-02 15:46:14 +02:00
drw = drw_create ( dpy , screen , root , sw , sh ) ;
2020-08-02 15:46:12 +02:00
if ( ! drw_fontset_create ( drw , fonts , LENGTH ( fonts ) ) )
die ( " no fonts could be loaded. " ) ;
2020-08-02 15:46:14 +02:00
drw_setscheme ( drw , scheme [ SchemeNorm ] ) ;
//find an unused keycode to use as a temporary keycode (derived from source: https://stackoverflow.com/questions/44313966/c-xtest-emitting-key-presses-for-every-unicode-character)
KeySym * keysyms = NULL ;
int keysyms_per_keycode = 0 ;
int keycode_low , keycode_high ;
Bool key_is_empty ;
int symindex ;
XDisplayKeycodes ( dpy , & keycode_low , & keycode_high ) ;
keysyms = XGetKeyboardMapping ( dpy , keycode_low , keycode_high - keycode_low , & keysyms_per_keycode ) ;
for ( i = keycode_low ; i < = keycode_high ; i + + ) {
key_is_empty = True ;
for ( j = 0 ; j < keysyms_per_keycode ; j + + ) {
symindex = ( i - keycode_low ) * keysyms_per_keycode + j ;
if ( keysyms [ symindex ] ! = 0 ) {
key_is_empty = False ;
} else {
break ;
}
}
if ( key_is_empty ) {
tmp_keycode = i ;
break ;
}
}
2020-08-02 15:46:12 +02:00
/* init appearance */
for ( j = 0 ; j < SchemeLast ; j + + )
scheme [ j ] = drw_scm_create ( drw , colors [ j ] , 2 ) ;
2008-07-15 18:53:38 +02:00
2011-03-25 15:14:12 +01:00
/* init atoms */
2011-10-09 17:59:03 +02:00
if ( isdock ) {
netatom [ NetWMWindowType ] = XInternAtom ( dpy ,
" _NET_WM_WINDOW_TYPE " , False ) ;
2011-04-06 09:03:25 +02:00
atype = XInternAtom ( dpy , " _NET_WM_WINDOW_TYPE_DOCK " , False ) ;
2011-10-09 17:59:03 +02:00
}
2011-03-24 18:14:07 +01:00
2011-03-25 15:14:12 +01:00
/* init appearance */
countrows ( ) ;
if ( ! ww )
2011-10-09 17:59:03 +02:00
ww = sw ;
2011-03-25 15:14:12 +01:00
if ( ! wh )
2020-08-02 15:46:16 +02:00
wh = sh * rows / heightfactor ;
2011-10-09 17:59:03 +02:00
if ( ! wx )
wx = 0 ;
if ( wx < 0 )
wx = sw + wx - ww ;
2011-03-25 15:14:12 +01:00
if ( ! wy )
wy = sh - wh ;
2011-04-04 13:39:31 +02:00
if ( wy < 0 )
wy = sh + wy - wh ;
2020-08-02 15:46:16 +02:00
for ( i = 0 ; i < numkeys ; i + + )
2008-07-15 22:12:55 +02:00
keys [ i ] . pressed = 0 ;
2008-07-15 18:53:38 +02:00
2011-03-25 15:14:12 +01:00
wa . override_redirect = ! wmborder ;
2020-08-02 15:46:12 +02:00
wa . border_pixel = scheme [ SchemeNorm ] [ ColFg ] . pixel ;
wa . background_pixel = scheme [ SchemeNorm ] [ ColBg ] . pixel ;
2011-02-01 20:04:02 +01:00
win = XCreateWindow ( dpy , root , wx , wy , ww , wh , 0 ,
2020-08-02 15:46:14 +02:00
CopyFromParent , CopyFromParent , CopyFromParent ,
CWOverrideRedirect | CWBorderPixel |
CWBackingPixel , & wa ) ;
2010-05-16 12:17:09 +02:00
XSelectInput ( dpy , win , StructureNotifyMask | ButtonReleaseMask |
2011-10-09 17:22:50 +02:00
ButtonPressMask | ExposureMask | LeaveWindowMask |
PointerMotionMask ) ;
2011-03-25 15:14:12 +01:00
2008-07-15 22:12:55 +02:00
wmh = XAllocWMHints ( ) ;
wmh - > input = False ;
wmh - > flags = InputHint ;
2011-10-09 17:45:12 +02:00
if ( ! isdock ) {
sizeh = XAllocSizeHints ( ) ;
sizeh - > flags = PMaxSize | PMinSize ;
sizeh - > min_width = sizeh - > max_width = ww ;
sizeh - > min_height = sizeh - > max_height = wh ;
}
2011-03-25 15:14:12 +01:00
XStringListToTextProperty ( & name , 1 , & str ) ;
ch = XAllocClassHint ( ) ;
ch - > res_class = name ;
ch - > res_name = name ;
2011-10-09 17:45:12 +02:00
XSetWMProperties ( dpy , win , & str , & str , NULL , 0 , sizeh , wmh ,
2011-03-25 15:14:12 +01:00
ch ) ;
2020-08-02 15:46:14 +02:00
XFree ( keysyms ) ;
2011-03-25 15:14:12 +01:00
XFree ( ch ) ;
2008-07-15 22:12:55 +02:00
XFree ( wmh ) ;
2011-03-25 15:14:12 +01:00
XFree ( str . value ) ;
2011-10-09 17:45:12 +02:00
if ( sizeh ! = NULL )
XFree ( sizeh ) ;
if ( isdock ) {
XChangeProperty ( dpy , win , netatom [ NetWMWindowType ] , XA_ATOM ,
32 , PropModeReplace ,
( unsigned char * ) & atype , 1 ) ;
}
2011-03-25 15:14:12 +01:00
2008-07-15 18:53:38 +02:00
XMapRaised ( dpy , win ) ;
2020-08-02 15:46:12 +02:00
drw_resize ( drw , ww , wh ) ;
2008-07-15 22:12:55 +02:00
updatekeys ( ) ;
2008-07-15 18:53:38 +02:00
drawkeyboard ( ) ;
}
2008-07-15 22:12:55 +02:00
void
updatekeys ( ) {
2011-03-25 15:14:12 +01:00
int i , j ;
2011-04-13 09:46:51 +02:00
int x = 0 , y = 0 , h , base , r = rows ;
2008-07-15 22:12:55 +02:00
2011-04-10 13:36:41 +02:00
h = ( wh - 1 ) / rows ;
2020-08-02 15:46:16 +02:00
for ( i = 0 ; i < numkeys ; i + + , r - - ) {
for ( j = i , base = 0 ; j < numkeys & & keys [ j ] . keysym ! = 0 ; j + + )
2008-07-15 22:12:55 +02:00
base + = keys [ j ] . width ;
2020-08-02 15:46:16 +02:00
for ( x = 0 ; i < numkeys & & keys [ i ] . keysym ! = 0 ; i + + ) {
2008-07-15 22:12:55 +02:00
keys [ i ] . x = x ;
keys [ i ] . y = y ;
2011-04-10 13:36:41 +02:00
keys [ i ] . w = keys [ i ] . width * ( ww - 1 ) / base ;
2011-04-13 19:09:53 +02:00
keys [ i ] . h = r = = 1 ? wh - y - 1 : h ;
2008-07-15 22:12:55 +02:00
x + = keys [ i ] . w ;
}
if ( base ! = 0 )
2011-04-10 13:36:41 +02:00
keys [ i - 1 ] . w = ww - 1 - keys [ i - 1 ] . x ;
2008-07-15 22:12:55 +02:00
y + = h ;
}
}
2011-03-24 19:41:58 +01:00
void
usage ( char * argv0 ) {
2020-08-02 15:46:16 +02:00
fprintf ( stderr , " usage: %s [-hdvDO] [-g geometry] [-fn font] [-l layers] [-s initial_layer] \n " , argv0 ) ;
2020-08-02 15:46:15 +02:00
fprintf ( stderr , " Options: \n " ) ;
fprintf ( stderr , " -d - Set Dock Window Type \n " ) ;
fprintf ( stderr , " -D - Enable debug \n " ) ;
fprintf ( stderr , " -O - Disable overlays \n " ) ;
fprintf ( stderr , " -l - Comma separated list of layers to enable \n " ) ;
2020-08-02 15:46:16 +02:00
fprintf ( stderr , " -s - Layer to select on program start \n " ) ;
fprintf ( stderr , " -H [int] - Height fraction, one key row takes 1/x of the screen height " ) ;
2020-08-02 15:46:15 +02:00
fprintf ( stderr , " -fn [font] - Set font (Xft, e.g: DejaVu Sans:bold:size=20) \n " ) ;
2011-03-24 19:41:58 +01:00
exit ( 1 ) ;
}
2020-08-02 15:46:16 +02:00
void setlayer ( ) {
numkeys = countkeys ( layers [ currentlayer ] ) ;
memcpy ( & keys , layers [ currentlayer ] , sizeof ( Key ) * numkeys ) ;
}
2020-08-02 15:46:07 +02:00
void
2020-08-02 15:46:14 +02:00
cyclelayer ( ) {
currentlayer + + ;
2020-08-02 15:46:15 +02:00
if ( currentlayer > = numlayers )
2020-08-02 15:46:14 +02:00
currentlayer = 0 ;
if ( debug ) { printf ( " Cycling to layer %d \n " , currentlayer ) ; fflush ( stdout ) ; }
2020-08-02 15:46:16 +02:00
setlayer ( ) ;
2020-08-02 15:46:14 +02:00
updatekeys ( ) ;
drawkeyboard ( ) ;
}
void
2020-08-02 15:46:15 +02:00
togglelayer ( ) {
if ( currentlayer > 0 ) {
currentlayer = 0 ;
} else if ( numlayers > 1 ) {
currentlayer = 1 ;
2020-08-02 15:46:14 +02:00
}
2020-08-02 15:46:15 +02:00
if ( debug ) { printf ( " Toggling layer %d \n " , currentlayer ) ; fflush ( stdout ) ; }
2020-08-02 15:46:16 +02:00
setlayer ( ) ;
2020-08-02 15:46:15 +02:00
updatekeys ( ) ;
drawkeyboard ( ) ;
2020-08-02 15:46:14 +02:00
}
2020-08-02 15:46:15 +02:00
2020-08-02 15:46:14 +02:00
void
showoverlay ( int idx ) {
if ( debug ) { printf ( " Showing overlay %d \n " , idx ) ; fflush ( stdout ) ; }
int i , j ;
//unpress existing key (visually only)
2020-08-02 15:46:16 +02:00
for ( i = 0 ; i < numkeys ; i + + ) {
2020-08-02 15:46:14 +02:00
if ( keys [ i ] . pressed & & ! IsModifierKey ( keys [ i ] . keysym ) ) {
keys [ i ] . pressed = 0 ;
drawkey ( & keys [ i ] ) ;
break ;
}
}
for ( i = idx , j = 0 ; i < OVERLAYS ; i + + , j + + ) {
if ( overlay [ i ] . keysym = = XK_Cancel ) {
break ;
}
while ( keys [ j ] . keysym = = 0 ) j + + ;
keys [ j ] . label = overlay [ i ] . label ;
keys [ j ] . keysym = overlay [ i ] . keysym ;
}
currentoverlay = idx ;
overlaykeysym = ispressingkeysym ;
releaseprotect = 1 ;
2020-08-02 15:46:07 +02:00
updatekeys ( ) ;
drawkeyboard ( ) ;
2020-08-02 15:46:14 +02:00
XSync ( dpy , False ) ;
}
void
hideoverlay ( ) {
if ( debug ) { printf ( " Hiding overlay %d \n " , currentoverlay ) ; fflush ( stdout ) ; }
currentoverlay = - 1 ;
overlaykeysym = 0 ;
currentlayer = - 1 ;
cyclelayer ( ) ;
2020-08-02 15:46:07 +02:00
}
2020-08-02 15:46:14 +02:00
2020-08-02 15:46:07 +02:00
void
sigterm ( int sig )
{
running = False ;
2020-08-02 15:46:09 +02:00
sigtermd = True ;
2020-08-02 15:46:15 +02:00
if ( debug ) { printf ( " Sigterm received \n " ) ; fflush ( stdout ) ; }
}
void
2020-08-02 15:46:16 +02:00
init_layers ( char * layer_names_list , const char * initial_layer_name ) {
int j ;
2020-08-02 15:46:15 +02:00
if ( layer_names_list = = NULL ) {
numlayers = LAYERS ;
memcpy ( & layers , & available_layers , sizeof ( available_layers ) ) ;
2020-08-02 15:46:16 +02:00
if ( initial_layer_name ! = NULL ) {
for ( j = 0 ; j < LAYERS ; j + + ) {
if ( strcmp ( layer_names [ j ] , initial_layer_name ) = = 0 ) {
currentlayer = j ;
break ;
}
}
}
2020-08-02 15:46:15 +02:00
} else {
char * s ;
s = strtok ( layer_names_list , " , " ) ;
while ( s ! = NULL ) {
if ( numlayers + 1 > LAYERS ) die ( " too many layers specified " ) ;
int found = 0 ;
for ( j = 0 ; j < LAYERS ; j + + ) {
if ( strcmp ( layer_names [ j ] , s ) = = 0 ) {
2020-08-02 15:46:16 +02:00
fprintf ( stderr , " Adding layer %s \n " , s ) ;
2020-08-02 15:46:15 +02:00
layers [ numlayers ] = available_layers [ j ] ;
2020-08-02 15:46:16 +02:00
if ( initial_layer_name ! = NULL & & strcmp ( layer_names [ j ] , initial_layer_name ) = = 0 ) {
currentlayer = numlayers ;
}
2020-08-02 15:46:15 +02:00
found = 1 ;
break ;
}
}
if ( ! found ) {
fprintf ( stderr , " Undefined layer: %s \n " , s ) ;
exit ( 3 ) ;
}
numlayers + + ;
s = strtok ( NULL , " , " ) ;
}
}
2020-08-02 15:46:16 +02:00
setlayer ( ) ;
2020-08-02 15:46:07 +02:00
}
2008-07-15 18:53:38 +02:00
int
main ( int argc , char * argv [ ] ) {
2011-10-09 17:36:09 +02:00
int i , xr , yr , bitm ;
unsigned int wr , hr ;
2020-08-02 15:46:16 +02:00
char * initial_layer_name = NULL ;
2020-08-02 15:46:15 +02:00
char * layer_names_list = NULL ;
2011-03-24 19:41:58 +01:00
2020-08-02 15:46:10 +02:00
signal ( SIGTERM , sigterm ) ;
2020-08-02 15:46:15 +02:00
2020-08-02 15:46:19 +02:00
2020-08-02 15:46:16 +02:00
//parse environment variables
2020-08-02 15:46:19 +02:00
if ( OVERLAYS < = 1 ) {
enableoverlays = 0 ;
} else {
const char * enableoverlays_env = getenv ( " SVKBD_ENABLEOVERLAYS " ) ;
if ( enableoverlays_env ! = NULL ) enableoverlays = atoi ( enableoverlays_env ) ;
}
2020-08-02 15:46:15 +02:00
const char * layers_env = getenv ( " SVKBD_LAYERS " ) ;
if ( layers_env ! = NULL ) {
layer_names_list = malloc ( 128 ) ;
2020-08-02 15:46:17 +02:00
if ( ! layer_names_list ) die ( " memory allocation error \n " ) ;
2020-08-02 15:46:15 +02:00
strcpy ( layer_names_list , layers_env ) ;
}
2020-08-02 15:46:16 +02:00
const char * heightfactor_s = getenv ( " SVKBD_HEIGHTFACTOR " ) ;
if ( heightfactor_s ! = NULL )
heightfactor = atoi ( heightfactor_s ) ;
2020-08-02 15:46:15 +02:00
2020-08-02 15:46:16 +02:00
//parse command line arguments
2011-03-24 19:41:58 +01:00
for ( i = 1 ; argv [ i ] ; i + + ) {
if ( ! strcmp ( argv [ i ] , " -v " ) ) {
2020-08-02 15:46:12 +02:00
die ( " svkbd- " VERSION " , © 2006-2020 svkbd engineers, "
2011-03-24 19:41:58 +01:00
" see LICENSE for details \n " ) ;
2011-10-09 17:45:12 +02:00
} else if ( ! strcmp ( argv [ i ] , " -d " ) ) {
isdock = True ;
2011-04-06 09:03:25 +02:00
continue ;
2011-10-09 18:00:21 +02:00
} else if ( ! strncmp ( argv [ i ] , " -g " , 2 ) ) {
2011-10-19 10:49:23 +02:00
if ( i > = argc - 1 )
continue ;
2011-10-09 17:36:09 +02:00
bitm = XParseGeometry ( argv [ i + 1 ] , & xr , & yr , & wr , & hr ) ;
if ( bitm & XValue )
wx = xr ;
if ( bitm & YValue )
wy = yr ;
if ( bitm & WidthValue )
ww = ( int ) wr ;
if ( bitm & HeightValue )
wh = ( int ) hr ;
2011-10-09 17:59:03 +02:00
if ( bitm & XNegative & & wx = = 0 )
wx = - 1 ;
if ( bitm & YNegative & & wy = = 0 )
wy = - 1 ;
2011-10-09 18:03:23 +02:00
i + + ;
2020-08-02 15:46:14 +02:00
} else if ( ! strcmp ( argv [ i ] , " -fn " ) ) { /* font or font set */
fonts [ 0 ] = argv [ + + i ] ;
} else if ( ! strcmp ( argv [ i ] , " -D " ) ) {
debug = 1 ;
2011-10-09 17:36:09 +02:00
} else if ( ! strcmp ( argv [ i ] , " -h " ) ) {
2011-03-24 19:41:58 +01:00
usage ( argv [ 0 ] ) ;
2020-08-02 15:46:15 +02:00
} else if ( ! strcmp ( argv [ i ] , " -O " ) ) {
enableoverlays = 0 ;
} else if ( ! strcmp ( argv [ i ] , " -l " ) ) {
if ( i > = argc - 1 )
continue ;
2020-08-02 15:46:17 +02:00
if ( layer_names_list = = NULL ) {
layer_names_list = malloc ( 128 ) ;
if ( ! layer_names_list ) die ( " memory allocation error \n " ) ;
}
2020-08-02 15:46:16 +02:00
strcpy ( layer_names_list , argv [ + + i ] ) ;
} else if ( ! strcmp ( argv [ i ] , " -s " ) ) {
if ( i > = argc - 1 )
continue ;
initial_layer_name = argv [ + + i ] ;
} else if ( ! strcmp ( argv [ i ] , " -H " ) ) {
if ( i > = argc - 1 )
continue ;
heightfactor = atoi ( argv [ + + i ] ) ;
} else {
fprintf ( stderr , " Invalid argument: %s \n " , argv [ i ] ) ;
exit ( 2 ) ;
2011-10-09 17:36:09 +02:00
}
2011-03-24 19:41:58 +01:00
}
2020-08-02 15:46:17 +02:00
if ( heightfactor < = 0 ) die ( " height factor must be a positive integer \n " ) ;
2020-08-02 15:46:16 +02:00
init_layers ( layer_names_list , initial_layer_name ) ;
2020-08-02 15:46:15 +02:00
2008-07-15 18:53:38 +02:00
if ( ! setlocale ( LC_CTYPE , " " ) | | ! XSupportsLocale ( ) )
fprintf ( stderr , " warning: no locale support \n " ) ;
if ( ! ( dpy = XOpenDisplay ( 0 ) ) )
die ( " svkbd: cannot open display \n " ) ;
setup ( ) ;
run ( ) ;
cleanup ( ) ;
XCloseDisplay ( dpy ) ;
2020-08-02 15:46:17 +02:00
free ( layer_names_list ) ;
2008-07-15 18:53:38 +02:00
return 0 ;
}