1212#include <stdint.h>
1313#include <getopt.h>
1414#include <sys/file.h>
15+ #include <stdarg.h>
1516#include <X11/Xlib.h>
1617#include <X11/extensions/XInput2.h>
1718#include <X11/extensions/XTest.h>
1819#include <X11/extensions/Xfixes.h>
1920
20- static const char * PROGRAM_VERSION = "1.0" ;
21- static int is_active = False ;
22- static int scrolls_since_active = 0 ;
23- #define ALT_KEY_CODE 64
21+ enum LogLevel
22+ {
23+ LOG_OFF ,
24+ LOG_FATAL ,
25+ LOG_ERROR ,
26+ LOG_WARN ,
27+ LOG_INFO ,
28+ LOG_DEBUG ,
29+ };
2430
2531enum ScrollDirection
2632{
@@ -52,6 +58,27 @@ struct Config {
5258 int trigger_key_modifiers ;
5359};
5460
61+ static const char * PROGRAM_VERSION = "1.0" ;
62+ static int is_active = False ;
63+ static int scrolls_since_active = 0 ;
64+ static enum LogLevel log_level = LOG_INFO ;
65+ #define CAPSLOCK_KEY_CODE 66
66+
67+ void logg (enum LogLevel level , const char * fmt , ...)
68+ {
69+ va_list args ;
70+ va_start (args , fmt );
71+ if ((level == LOG_ERROR && log_level >= LOG_ERROR ) || (level == LOG_FATAL && log_level >= LOG_FATAL ))
72+ {
73+ vfprintf (stderr , fmt , args );
74+ }
75+ else if (log_level >= level )
76+ {
77+ vfprintf (stdout , fmt , args );
78+ }
79+ va_end (args );
80+ }
81+
5582struct Config create_default_config ()
5683{
5784 struct Config cfg =
@@ -62,12 +89,25 @@ struct Config create_default_config()
6289 .show_debug_output = False ,
6390 .is_toggle_mode_on = False ,
6491 .release_trigger_button = True ,
65- .trigger_key_code = ALT_KEY_CODE ,
92+ .trigger_key_code = CAPSLOCK_KEY_CODE ,
6693 .trigger_key_modifiers = 0 ,
6794 };
6895 return cfg ;
6996}
7097
98+ void print_cfg (struct Config * cfg )
99+ {
100+ printf ("config:\n" );
101+ printf ("mouse_move_delta_to_scroll_threshold %i\n" , cfg -> mouse_move_delta_to_scroll_threshold );
102+ printf ("allow_horizontal_scroll %i\n" , cfg -> allow_horizontal_scroll );
103+ printf ("allow_triggering_of_repeated_scroll_event %i\n" , cfg -> allow_triggering_of_repeated_scroll_event );
104+ printf ("show_debug_output %i\n" , cfg -> show_debug_output );
105+ printf ("is_toggle_mode_on %i\n" , cfg -> is_toggle_mode_on );
106+ printf ("release_trigger_button %i\n" , cfg -> release_trigger_button );
107+ printf ("trigger_key_code %i\n" , cfg -> trigger_key_code );
108+ printf ("trigger_key_modifiers %i\n" , cfg -> trigger_key_modifiers );
109+ }
110+
71111void parse_args_into_config (int argc , char * * argv , struct Config * cfg ) {
72112 char * cvalue = NULL ;
73113 int c ;
@@ -81,7 +121,7 @@ void parse_args_into_config(int argc, char** argv, struct Config* cfg) {
81121 intmax_t num = strtoimax (optarg , NULL , 10 );
82122 if (errno == ERANGE )
83123 {
84- fprintf ( stderr , "error parsing value for -%c. It must be a positive integer." ,c );
124+ logg ( LOG_FATAL , "error parsing value for -%c. It must be a positive integer." , c );
85125 exit (-1 );
86126 }
87127 cfg -> mouse_move_delta_to_scroll_threshold = (uint ) labs (num );
@@ -93,22 +133,22 @@ void parse_args_into_config(int argc, char** argv, struct Config* cfg) {
93133 intmax_t num = strtoimax (optarg , NULL , 10 );
94134 if (errno == ERANGE )
95135 {
96- fprintf ( stderr , "error parsing value for -%c. It must be an integer." , c );
136+ logg ( LOG_FATAL , "error parsing value for -%c. It must be an integer." , c );
97137 exit (-1 );
98138 }
99139 cfg -> trigger_key_code = (int ) num ;
100140
101141 // look for optional modifier arg
102142 if (optind < argc && * argv [optind ] != '-' )
103143 {
104- num = strtoimax (argv [optind ], NULL , 0 );
105- optind ++ ;
106- if (errno == ERANGE )
107- {
108- fprintf ( stderr , "error parsing optional second value for -%c. It must be an integer." , c );
109- exit (-1 );
110- }
111- cfg -> trigger_key_modifiers = (int ) num ;
144+ num = strtoimax (argv [optind ], NULL , 0 );
145+ optind ++ ;
146+ if (errno == ERANGE )
147+ {
148+ logg ( LOG_FATAL , "error parsing optional second value for -%c. It must be an integer." , c );
149+ exit (-1 );
150+ }
151+ cfg -> trigger_key_modifiers = (int ) num ;
112152 }
113153 break ;
114154 }
@@ -139,6 +179,7 @@ void parse_args_into_config(int argc, char** argv, struct Config* cfg) {
139179 break ;
140180 case 'd' :
141181 cfg -> show_debug_output = True ;
182+ log_level = LOG_DEBUG ;
142183 break ;
143184 case 'v' :
144185 printf ("%s\n" , PROGRAM_VERSION );
@@ -167,8 +208,8 @@ static void request_to_receive_events(Display *dpy, Window win)
167208
168209 /* select for button and key events from all master devices */
169210 XISetMask (mask1 , XI_RawMotion );
170- // XISetMask(mask1, XI_ButtonPress);
171- // XISetMask(mask1, XI_ButtonRelease);
211+ // XISetMask(mask1, XI_ButtonPress);
212+ // XISetMask(mask1, XI_ButtonRelease);
172213 XISetMask (mask1 , XI_KeyPress );
173214 XISetMask (mask1 , XI_KeyRelease );
174215
@@ -189,13 +230,15 @@ static int has_xi2(Display *dpy)
189230 int minor = 2 ;
190231
191232 int rc = XIQueryVersion (dpy , & major , & minor );
192- if (rc == BadRequest ) {
193- fprintf (stderr , "No XI2 support. Server supports version %d.%d only.\n" , major , minor );
233+ if (rc == BadRequest )
234+ {
235+ logg (LOG_FATAL , "No XI2 support. Server supports version %d.%d only.\n" , major , minor );
194236 return 0 ;
195- } else if (rc != Success ) {
196- fprintf (stderr , "Internal Error! This is a bug in Xlib.\n" );
197237 }
198- // printf("XI2 supported. Server provides version %d.%d.\n", major, minor);
238+ else if (rc != Success )
239+ {
240+ logg (LOG_WARN , "Internal Error! This is a bug in Xlib.\n" );
241+ }
199242
200243 return 1 ;
201244}
@@ -207,11 +250,10 @@ void trigger_scroll(Display* display, struct Config* cfg, enum ScrollDirection s
207250{
208251 if (amount == 0 ) return ;
209252
210- if (cfg -> show_debug_output )
211- printf ("scroll %s, %dx %s\n" ,
212- scrollDirection == SCROLL_VERTICAL ? "v" : "h" ,
213- cfg -> allow_triggering_of_repeated_scroll_event ? abs (amount ) : 1 ,
214- amount < 0 ? "up" : "down" );
253+ logg (LOG_INFO , "scroll %s, %dx %s\n" ,
254+ scrollDirection == SCROLL_VERTICAL ? "v" : "h" ,
255+ cfg -> allow_triggering_of_repeated_scroll_event ? abs (amount ) : 1 ,
256+ amount < 0 ? "up" : "down" );
215257
216258 uint negative_scroll_amount_btn = scrollDirection == SCROLL_VERTICAL ? SCROLL_UP : SCROLL_LEFT ;
217259 uint postitive_scroll_amount_btn = scrollDirection == SCROLL_VERTICAL ? SCROLL_DOWN : SCROLL_RIGHT ;
@@ -248,7 +290,7 @@ int ensure_xinput2_or_exit(Display* display)
248290{
249291 int xi_opcode , event , error ;
250292 if (!XQueryExtension (display , "XInputExtension" , & xi_opcode , & event , & error )) {
251- fprintf ( stderr , "Error: X Input extension not available." );
293+ logg ( LOG_FATAL , "Error: X Input extension not available." );
252294 exit (-3 );
253295 }
254296
@@ -263,7 +305,7 @@ Display* open_display_or_exit()
263305 char * display_name = NULL ;
264306 Display * display ;
265307 if ((display = XOpenDisplay (display_name )) == NULL ) {
266- fprintf ( stderr , "Failed to open display.\n" );
308+ logg ( LOG_FATAL , "Failed to open display.\n" );
267309 exit (-1 );
268310 }
269311 return display ;
@@ -282,6 +324,8 @@ void set_is_active(Bool active, Display* display, Window window)
282324{
283325 if (active == is_active ) return ;
284326
327+ logg (LOG_INFO , active ? "activating\n" : "deactivating\n" );
328+
285329 is_active = active ;
286330 scrolls_since_active = 0 ; // reset
287331
@@ -294,12 +338,11 @@ void set_is_active(Bool active, Display* display, Window window)
294338
295339void check_for_scroll_trigger (enum ScrollDirection scroll_direction , double * total_movement_delta , double delta , struct Config * cfg , Display * display )
296340{
297- if (cfg -> show_debug_output )
298- printf ("check: dir: %s, total_movement_delta: %g, delta: %g, thres: %d\n" ,
299- scroll_direction == SCROLL_VERTICAL ? "v" : "h" ,
300- * total_movement_delta ,
301- delta ,
302- cfg -> mouse_move_delta_to_scroll_threshold );
341+ logg (LOG_DEBUG , "check: dir: %s, total_movement_delta: %g, delta: %g, thres: %d\n" ,
342+ scroll_direction == SCROLL_VERTICAL ? "v" : "h" ,
343+ * total_movement_delta ,
344+ delta ,
345+ cfg -> mouse_move_delta_to_scroll_threshold );
303346
304347 * total_movement_delta += delta ;
305348 if (fabs (* total_movement_delta ) > cfg -> mouse_move_delta_to_scroll_threshold )
@@ -325,7 +368,7 @@ int find_input_device_id_by_name(Display* display, const char* device_name)
325368 XDeviceInfo * dd = XListInputDevices (display , & numDevices );
326369 for (int i = 0 ; i < numDevices ; i ++ )
327370 {
328- // printf("%s %ld %ld\n", dd[i].name, dd[i].id);
371+ // printf("%s %ld %ld\n", dd[i].name, dd[i].id);
329372 fflush (stdout );
330373 if (strcasecmp (dd [i ].name , device_name ) == 0 )
331374 {
@@ -339,7 +382,7 @@ int find_input_device_id_by_name(Display* display, const char* device_name)
339382
340383Bool is_trigger_shortcut (int key_code , int modifiers , struct Config * cfg )
341384{
342- // printf("k %d, mod %d", key_code, modifiers);
385+ // printf("k %d, mod %d", key_code, modifiers);
343386 if (cfg -> trigger_key_modifiers > 0 // need to check modifiers?
344387 && (modifiers & cfg -> trigger_key_modifiers ) != modifiers ) // check modifiers
345388 {
@@ -366,18 +409,22 @@ void ensure_single_instance_or_exit()
366409int main (int argc , char * * argv )
367410{
368411 ensure_single_instance_or_exit ();
369- Display * display = open_display_or_exit ();
370- int xi_opcode = ensure_xinput2_or_exit (display );
371412
372413 struct Config cfg = create_default_config ();
373414 parse_args_into_config (argc , argv , & cfg );
374415
416+ if (cfg .show_debug_output )
417+ print_cfg (& cfg );
418+
419+ Display * display = open_display_or_exit ();
420+ int xi_opcode = ensure_xinput2_or_exit (display );
421+
375422 Window window = DefaultRootWindow (display );
376423 request_to_receive_events (display , window );
377424
378425 int xtest_keyboard_device_id = find_input_device_id_by_name (display , "Virtual core XTEST keyboard" );
379426 if (xtest_keyboard_device_id == -1 )
380- printf ( "warn: could not find 'Virtual core XTEST keyboard'. Things might not work well." );
427+ logg ( LOG_WARN , " could not find 'Virtual core XTEST keyboard'. Things might not work well." );
381428
382429 struct ScreenPoint start_pointer_pos = get_pointer_position (display , window );
383430
@@ -403,7 +450,7 @@ int main(int argc, char **argv)
403450 int key_code = event -> detail ;
404451 Bool is_repeat = event -> flags & XIKeyRepeat ;
405452 if (cfg .show_debug_output )
406- printf ( "KeyPress: key_code %d, mods %d, is_repeat %d\n" , key_code , event -> mods .base , is_repeat );
453+ logg ( LOG_DEBUG , "KeyPress: key_code %d, mods %d, is_repeat %d\n" , key_code , event -> mods .base , is_repeat );
407454
408455 if (event -> deviceid != xtest_keyboard_device_id
409456 && is_trigger_shortcut (key_code , event -> mods .base , & cfg )
@@ -425,11 +472,11 @@ int main(int argc, char **argv)
425472 }
426473 case XI_KeyRelease :
427474 {
428- printf ("KEY RELEASE" );
429475 if (!cfg .is_toggle_mode_on )
430476 {
431477 XIDeviceEvent * event = (XIDeviceEvent * ) cookie -> data ;
432478 int key_code = event -> detail ;
479+ logg (LOG_DEBUG , "KeyRelease: key_code %d, mods %d\n" , key_code , event -> mods .base );
433480 if (event -> deviceid != xtest_keyboard_device_id
434481 && is_active
435482 && is_trigger_shortcut (key_code , 0 , & cfg ))
@@ -444,6 +491,7 @@ int main(int argc, char **argv)
444491 case XI_RawMotion :
445492 if (!is_active )
446493 break ;
494+
447495 /// fixate pointer (set pointer to start pos): not the best solution (is wiggles a bit) but I've not found a bette one (maybe hide the cursor while scrolling to hide the wiggling)
448496 XWarpPointer (display , None , window , 0 , 0 , 0 , 0 ,
449497 start_pointer_pos .x , start_pointer_pos .y );
0 commit comments