From 5230c7448d6ff3f6c5c48547a63a71ad87234ae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20M=C3=A9tras?= Date: Mon, 30 Dec 2019 18:43:46 -0500 Subject: [PATCH 01/10] Driver launcher --- TabletDriver | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100755 TabletDriver diff --git a/TabletDriver b/TabletDriver new file mode 100755 index 0000000..4631157 --- /dev/null +++ b/TabletDriver @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +# Adjust this as needed +DRIVER="/usr/local/huion-linux-drivers/huion-tablet-driver.py" +CONFIG_FILE="/usr/local/huion-linux-drivers/config.ini" + +T="256c:006e" +BUS=$(lsusb | grep "$T" | sed -e 's|Bus \([0-9]*\) Device \([0-9]*\):.*$|\1|g') +DEV=$(lsusb | grep "$T" | sed -e 's|Bus \([0-9]*\) Device \([0-9]*\):.*$|\2|g') + +sudo rmmod hid_uclogic 2>/dev/null +sudo modprobe uinput +sudo uclogic-probe $BUS $DEV | uclogic-decode +sleep 3 # wait until device is released again + +# Create new pencil pointer on the tablet +# The following code has been added into the Python code +#INPD=$(xinput | grep 'Tablet Monitor Pen' | cut -f 2) +#INPD=${INPD:3} +#OUTPD=$(xrandr --listactivemonitors | sort | cut -f 6 -d ' ' | head -2 | tail -1) +#xinput map-to-output ${INPD:-'15'} ${OUTPD:-'HDMI-1'} + +sudo python3 $DRIVER $CONFIG_FILE From 07c5cc22c8c9ef530c43022e0e24ac9e068bfba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20M=C3=A9tras?= Date: Mon, 30 Dec 2019 18:47:24 -0500 Subject: [PATCH 02/10] Add new 5 buttons menu for GT116 --- config.ini | 50 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/config.ini b/config.ini index 6dad20e..68686c2 100644 --- a/config.ini +++ b/config.ini @@ -16,7 +16,7 @@ # Your tablet. Find the supported models at the end of this file. # Or use [tablet_debug] for just printing input info for sharing. -current_tablet = [tablet_gt221pro] +current_tablet = [tablet_gt116] # Configure buttons enable_buttons = true @@ -31,6 +31,9 @@ enable_multi_monitor = false enable_xrandr = false current_monitor_setup = [monitor_2] +# Multi Pointers +enable_multi_pointer = true + # Calibration Data enable_calibration = false calibrate_min_x = 250 @@ -47,12 +50,12 @@ scrollbar_notifications = false uclogic_bins = /usr/local/bin refresh_rate_fps = 300 -debug_mode = true +debug_mode = false # Here you can select a menu with the appropriate number of buttons for your tablet. E.g.: #start_menu = [menu_simple_4b] #start_menu = [menu_main_10b] -start_menu = +start_menu = [menu_5b_multi] # @@ -71,6 +74,41 @@ su = key ctrl+minus # zoom out (krita) sd = key ctrl+plus # zoom in (krita) +[menu_5b_multi] +title = % Main Menu % +b0 = [menu_5b_krita] +b1 = [menu_5b_gimp] +b2 = key ctrl+s # save +b3 = key ctrl+o # open +b4 = key Escape +# scrollbar (up/down) +su = click 4 # mouse wheel up +sd = click 5 # mouse wheel down + +[menu_5b_krita] +title = % Krita % +b0 = key Tab # hide interface +b1 = key r # pick layer +b2 = key ctrl+z # undo +b3 = key ctrl+shift+z # redo +b4 = [menu_5b_multi] +# scrollbar (up/down) +su = key ctrl+minus # zoom out +sd = key ctrl+plus # zoom in + +[menu_5b_gimp] +title = % Gimp % +b0 = key Tab # hide interface +b1 = key r # rect select +b2 = key ctrl+z # undo +b3 = key ctrl+y # redo +b4 = [menu_5b_multi] +# +# scrollbar (up/down) +su = key minus # zoom out +sd = key plus # zoom in + + [menu_simple_10b] # upper buttons b0 = key Tab # hide interface @@ -289,8 +327,8 @@ xrandr_args = ${xrandr_output1} ${xrandr_output2} # 2 monitors arranged horizontally # screens widths and heights -screen_1W = 2560 -screen_1H = 1440 +screen_1W = 1366 +screen_1H = 768 screen_2W = 1920 screen_2H = 1080 @@ -386,7 +424,7 @@ pen_max_x = 53580 pen_max_y = 33640 pen_max_z = 8191 resolution = 5080 -buttons = 4 +buttons = 5 scrollbar = 1 [tablet_gt133] From 113cc22c16636e288c98d6335b5507060b855fe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20M=C3=A9tras?= Date: Mon, 30 Dec 2019 19:00:51 -0500 Subject: [PATCH 03/10] Add calibrate #49 and multi-pointer --- huion-tablet-driver.py | 103 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 97 insertions(+), 6 deletions(-) diff --git a/huion-tablet-driver.py b/huion-tablet-driver.py index 7624aca..a230ff7 100755 --- a/huion-tablet-driver.py +++ b/huion-tablet-driver.py @@ -12,7 +12,7 @@ from time import gmtime, strftime, sleep MENU = {} - +XINPUT_DEVICE_KEYWORD = "Tablet Monitor Pen" # ----------------------------------------------------------------------------- class main(): @@ -32,6 +32,8 @@ def run(): setup_driver() calibrate() multi_monitor() + calibrate_mapping() + multi_pointer() main_loop() @@ -180,6 +182,11 @@ def setup_driver(): else: print("\tScreen disabled") + if main.settings['enable_multi_pointer']: + print("\tMulti Pointer ENABLED") + else: + print("\tMulti Pointer disabled") + if main.settings['debug_mode']: print("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") print("\t\t\t< DEBUG MODE ENABLED >") @@ -259,6 +266,75 @@ def multi_monitor(): main.settings['screen_width'], main.settings['screen_height'], main.settings['tablet_offset_x'], main.settings['tablet_offset_y'])) + +# ----------------------------------------------------------------------------- +def multi_pointer(xinput_device_keyword=XINPUT_DEVICE_KEYWORD): + """ + """ + + if not main.settings['enable_multi_pointer']: + return + + print("\nSetting up multiple pointers. . . ") + + # Removing previous existing master + cmd = "xinput --remove-master HuionTablet" + if main.settings['debug_mode']: + print('» {}'.format(cmd)) + os.popen(cmd) + + if main.settings['enable_multi_pointer']: + print("Create master input. . .") + cmd = "xinput create-master 'HuionTablet'" + if main.settings['debug_mode']: + print('» {}'.format(cmd)) + os.popen(cmd) + + xinput_device = os.popen("xinput | grep '%s' | cut -f 2" % xinput_device_keyword).read() + xinput_device = xinput_device.split('\n')[0][3:] # Chose the first instance of keyword, strips off "id=" + master = os.popen("xinput | grep 'HuionTablet pointer' | cut -f 2").read() + master = master.split('\n')[0][3:] + if main.settings['debug_mode']: + print('» input device = {}'.format(xinput_device)) + + cmd = "xinput reattach {} {}".format(xinput_device, master) + if main.settings['debug_mode']: + print('» {}'.format(cmd)) + os.popen(cmd) + + +# ----------------------------------------------------------------------------- +def calibrate_mapping(xinput_device_keyword=XINPUT_DEVICE_KEYWORD): + + try: + # Locate tablet pen device number + xinput_device = os.popen("xinput | grep '%s' | cut -f 2" % xinput_device_keyword).read() + xinput_device = xinput_device.split('\n')[0][3:] # Chose the first instance of keyword, strips off "id=" + + # Locate output device name + xoutput_devices = os.popen("xrandr --listactivemonitors | sort | cut -f 6 -d ' ' ").read() + xoutput_devices = list(filter(None, xoutput_devices.split('\n'))) # Assumes the first device is the main monitor and the second is the graphic monitor + xoutput_device = xoutput_devices[1] + + # Lenovo's defaults + if xinput_device == '': + xinput_device = 15 + if xoutput_device == '': + xoutput_device = "HDMI-1" + + # Calibrate by mapping input to graphic pad output + os.system("xinput map-to-output %s %s" % (xinput_device, xoutput_device)) + print() + sys.stdout.write("Calibrating graphics monitor via xinput mapping. . .") + except: + print("Error with calibrate_mapping") + + print("Done") + print("xinput_device = %s" % xinput_device) + print("xoutput_device = %s" % xoutput_device) + print("xoutput_mainscreen = %s" % xoutput_devices[0]) + + # ----------------------------------------------------------------------------- def calibrate(): @@ -384,7 +460,10 @@ def main_loop(): # convert to the exponent (0, 1, 2, 3, 4...) BUTTON_VAL = int(math.log(BUTTON_VAL, 2)) if main.current_menu: - do_shortcut("button", MENU[main.current_menu][BUTTON_VAL]) + try: + do_shortcut("button", MENU[main.current_menu][BUTTON_VAL]) + except: + print("Unknown key: MENU[%s][%d]" % (main.current_menu, BUTTON_VAL)) # SCROLLBAR EVENT @@ -495,7 +574,7 @@ def keypress(title, sequence): try: sp.run(cmd, shell=True, check=True) except sp.CalledProcessError as e: - run_error(e, cmd) + run_error(e, cmd, False) # ----------------------------------------------------------------------------- @@ -520,7 +599,7 @@ def switch_menu(new_menu): try: sp.run(cmd, shell=True, check=True) except sp.CalledProcessError as e: - run_error(e, cmd) + run_error(e, cmd, False) # ----------------------------------------------------------------------------- @@ -541,9 +620,15 @@ def read_config(): sys.stdout.write("Reading configuration. . . ") - if os.path.exists('config.ini'): + # Config file can be given as command line parameter + config_file = '' + if len(sys.argv) > 1: + config_file = sys.argv[1] + print("Config file is %s" % config_file) + + if os.path.exists('config.ini') or config_file != '': config = ConfigParser(interpolation=ExtendedInterpolation()) - config.read('config.ini') + config.read([config_file, 'config.ini']) else: print("ERROR: Couldn't locate config.ini") sys.exit(2) @@ -683,6 +768,12 @@ def read_config(): except: current_monitor_setup = "none" + # multi pointer + try: + main.settings['enable_multi_pointer'] = config.get('config', 'enable_multi_pointer') + except: + main.settings['enable_multi_pointer'] = False + # tablet calibration try: From f56185fd1ed0cce6e1fefb8ea1c54e8063518ad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20M=C3=A9tras?= Date: Mon, 30 Dec 2019 20:16:23 -0500 Subject: [PATCH 04/10] Update with GT116 example on Ubuntu --- README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/README.md b/README.md index 2635136..d794f41 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,36 @@ b9 = key 6 # turn right (krita) [See an example with multiple menus in the wiki](https://github.com/joseluis/huion-linux-drivers/wiki/Buttons-Shortcuts#12-example-with-multiple-menus) +## Ubuntu example for Huion Kamvas Pro 12" GT-116 +*Tested on Ubuntu LTS 16.04* + +First, adapt the parameters in `config.ini` to your tablet model. In case of a Huion Kamvas Pro 12" (GT-116), you could change the following options to use the 5 buttons menu for Gimp and Krita. +```ini +current_tablet = [tablet_gt116] +... +enable_multi_pointer = true +... +start_menu = [menu_5b_multi] +``` + +After doing all the pre-requisites listed above, copy the 3 files `TabletDriver`, `config.ini` and `python-tablet-driver.py` into `/usr/local/huion-linux-driver`. +```bash +$ sudo mkdir /usr/local/huion-linux-driver +$ sudo cp config.ini /usr/local/huion-linux-driver +$ sudo cp python-table-driver.py /usr/local/huion-linux-driver +$ sudo cp TabletDriver /usr/local/huion-linux-driver +``` + +Create a symbolic link to the launcher file `TabletDriver`: +```bash +$ sudo ln -s /usr/local/huion-linux-driver/TabletDriver /usr/local/bin/TabletDriver +``` + +Then you can launch the driver: +```bash +$ sudo TableDriver +``` +The console screen lists the configuration options and then displays the current active menu. You can change the active menu using the lower left button. ## Help Welcomed [↑](#table-of-contents "Back to TOC") From 1fe52559cded2acdb5596ee59f2e6118f3101856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20M=C3=A9tras?= Date: Mon, 30 Dec 2019 20:33:06 -0500 Subject: [PATCH 05/10] Add Ubuntu example into TOC --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d794f41..289fc17 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ - [Config Examples](#config-examples-) - [Multi-Monitor](#multi-monitor-) - [Shortcuts](#shortcuts-) +- [Ubuntu Example for Huion Kamvas Pro 12"](#ubuntu-example-) - [Help Welcomed](#help-welcomed-) - [To do](#to-do-) @@ -163,7 +164,7 @@ b9 = key 6 # turn right (krita) [See an example with multiple menus in the wiki](https://github.com/joseluis/huion-linux-drivers/wiki/Buttons-Shortcuts#12-example-with-multiple-menus) -## Ubuntu example for Huion Kamvas Pro 12" GT-116 +## Ubuntu Example for Huion Kamvas Pro 12" [↑](#table-of-contents "Back to TOC") *Tested on Ubuntu LTS 16.04* First, adapt the parameters in `config.ini` to your tablet model. In case of a Huion Kamvas Pro 12" (GT-116), you could change the following options to use the 5 buttons menu for Gimp and Krita. @@ -192,7 +193,7 @@ Then you can launch the driver: ```bash $ sudo TableDriver ``` -The console screen lists the configuration options and then displays the current active menu. You can change the active menu using the lower left button. +The console screen lists the configuration options and then displays the current active menu. You can change the active menu using the lower left button. This configuration creates 2 pointers: one for the mouse that you can keep on your primary screen and one for the tablet pencil that you use to draw on the tablet. The mouse can move on both screens: if you loose it, it could be on the tablet screen. ## Help Welcomed [↑](#table-of-contents "Back to TOC") From 486fca12f75f6fbc80fbac86ee8ffd3e9f1848ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20M=C3=A9tras?= Date: Mon, 30 Dec 2019 20:38:50 -0500 Subject: [PATCH 06/10] Ubuntu example into TOC take 2 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 289fc17..c6d2c17 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ - [Config Examples](#config-examples-) - [Multi-Monitor](#multi-monitor-) - [Shortcuts](#shortcuts-) -- [Ubuntu Example for Huion Kamvas Pro 12"](#ubuntu-example-) +- [Ubuntu Example for Huion Kamvas Pro 12"](#ubuntu-example-for-gt-116-) - [Help Welcomed](#help-welcomed-) - [To do](#to-do-) @@ -164,7 +164,7 @@ b9 = key 6 # turn right (krita) [See an example with multiple menus in the wiki](https://github.com/joseluis/huion-linux-drivers/wiki/Buttons-Shortcuts#12-example-with-multiple-menus) -## Ubuntu Example for Huion Kamvas Pro 12" [↑](#table-of-contents "Back to TOC") +## Ubuntu Example for GT-116 [↑](#table-of-contents "Back to TOC") *Tested on Ubuntu LTS 16.04* First, adapt the parameters in `config.ini` to your tablet model. In case of a Huion Kamvas Pro 12" (GT-116), you could change the following options to use the 5 buttons menu for Gimp and Krita. From 6b3ea431a982e12f4fc5c5d910393429890358c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20M=C3=A9tras?= Date: Thu, 2 Jan 2020 21:43:24 -0500 Subject: [PATCH 07/10] Use the last display instead of the second --- huion-tablet-driver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/huion-tablet-driver.py b/huion-tablet-driver.py index a230ff7..4f2ed88 100755 --- a/huion-tablet-driver.py +++ b/huion-tablet-driver.py @@ -314,7 +314,7 @@ def calibrate_mapping(xinput_device_keyword=XINPUT_DEVICE_KEYWORD): # Locate output device name xoutput_devices = os.popen("xrandr --listactivemonitors | sort | cut -f 6 -d ' ' ").read() xoutput_devices = list(filter(None, xoutput_devices.split('\n'))) # Assumes the first device is the main monitor and the second is the graphic monitor - xoutput_device = xoutput_devices[1] + xoutput_device = xoutput_devices[-1] # Lenovo's defaults if xinput_device == '': From a6784df48feaec04adc422e4dae654013abe45b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20M=C3=A9tras?= Date: Thu, 2 Jan 2020 22:06:19 -0500 Subject: [PATCH 08/10] Cleanup multi cursor at program exit (NOT TESTED) --- huion-tablet-driver.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/huion-tablet-driver.py b/huion-tablet-driver.py index 4f2ed88..cfb330f 100755 --- a/huion-tablet-driver.py +++ b/huion-tablet-driver.py @@ -10,6 +10,7 @@ import numexpr from configparser import ConfigParser, ExtendedInterpolation from time import gmtime, strftime, sleep +import atexit MENU = {} XINPUT_DEVICE_KEYWORD = "Tablet Monitor Pen" @@ -270,6 +271,9 @@ def multi_monitor(): # ----------------------------------------------------------------------------- def multi_pointer(xinput_device_keyword=XINPUT_DEVICE_KEYWORD): """ + Set up multi pointer: + - The mouse can be used on the main display and the tablet. + - The stylet is used on the tablet. """ if not main.settings['enable_multi_pointer']: @@ -277,13 +281,9 @@ def multi_pointer(xinput_device_keyword=XINPUT_DEVICE_KEYWORD): print("\nSetting up multiple pointers. . . ") - # Removing previous existing master - cmd = "xinput --remove-master HuionTablet" - if main.settings['debug_mode']: - print('» {}'.format(cmd)) - os.popen(cmd) - if main.settings['enable_multi_pointer']: + atexit.register(cleanup_multi_pointer) + print("Create master input. . .") cmd = "xinput create-master 'HuionTablet'" if main.settings['debug_mode']: @@ -303,6 +303,18 @@ def multi_pointer(xinput_device_keyword=XINPUT_DEVICE_KEYWORD): os.popen(cmd) +# ----------------------------------------------------------------------------- +def cleanup_multi_pointer(): + """ + Cleanup multi pointer at program exit. + """ + # Removing existing master + cmd = "xinput --remove-master HuionTablet" + if main.settings['debug_mode']: + print('» {}'.format(cmd)) + os.popen(cmd) + + # ----------------------------------------------------------------------------- def calibrate_mapping(xinput_device_keyword=XINPUT_DEVICE_KEYWORD): From dc8791c487114f1f458640ab5b3818c0be5cc40c Mon Sep 17 00:00:00 2001 From: Shervin Sahba Date: Sun, 19 Jan 2020 01:44:27 -0800 Subject: [PATCH 09/10] Create constant XOUTPUT_DEVICE_ARGUMENT to control calibrate_mapping Introduces a constant so that users can specify their output monitor for calibrate_mapping(). This expands on the bug fix mentioned here https://github.com/joseluis/huion-linux-drivers/pull/53#discussion_r362628886 --- huion-tablet-driver.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/huion-tablet-driver.py b/huion-tablet-driver.py index cfb330f..a8d61cd 100755 --- a/huion-tablet-driver.py +++ b/huion-tablet-driver.py @@ -13,7 +13,10 @@ import atexit MENU = {} -XINPUT_DEVICE_KEYWORD = "Tablet Monitor Pen" +XINPUT_DEVICE_KEYWORD = "Tablet Monitor Pen" # your pen's name in the terminal output of xinput +XOUTPUT_DEVICE_ARGUMENT = -1 # defaults to final active monitor in terminal output of xrandr + # ... change to select specific monitor in xoutput_devices + # ----------------------------------------------------------------------------- class main(): @@ -316,8 +319,11 @@ def cleanup_multi_pointer(): # ----------------------------------------------------------------------------- -def calibrate_mapping(xinput_device_keyword=XINPUT_DEVICE_KEYWORD): - +def calibrate_mapping(xinput_device_keyword=XINPUT_DEVICE_KEYWORD, xoutput_device_argument=XOUTPUT_DEVICE_ARGUMENT): + """ + Maps tablet pen, specified by xinput_device_keyword, to a given monitor, specified by xoutput_device_argument. + If xoutput_device_argument=-1, the pen's bounds will calibrate to the last active monitor in xrandr. + """ try: # Locate tablet pen device number xinput_device = os.popen("xinput | grep '%s' | cut -f 2" % xinput_device_keyword).read() @@ -325,8 +331,8 @@ def calibrate_mapping(xinput_device_keyword=XINPUT_DEVICE_KEYWORD): # Locate output device name xoutput_devices = os.popen("xrandr --listactivemonitors | sort | cut -f 6 -d ' ' ").read() - xoutput_devices = list(filter(None, xoutput_devices.split('\n'))) # Assumes the first device is the main monitor and the second is the graphic monitor - xoutput_device = xoutput_devices[-1] + xoutput_devices = list(filter(None, xoutput_devices.split('\n'))) # lists all active monitors + xoutput_device = xoutput_devices[xoutput_device_argument] # Lenovo's defaults if xinput_device == '': From 22f3babc6d881bceb0000e2d584a6460863d4cc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20M=C3=A9tras?= Date: Mon, 17 Feb 2020 04:50:45 -0500 Subject: [PATCH 10/10] Tested multi-pointer cleanup --- huion-tablet-driver.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/huion-tablet-driver.py b/huion-tablet-driver.py index cfb330f..2e311d1 100755 --- a/huion-tablet-driver.py +++ b/huion-tablet-driver.py @@ -282,7 +282,7 @@ def multi_pointer(xinput_device_keyword=XINPUT_DEVICE_KEYWORD): print("\nSetting up multiple pointers. . . ") if main.settings['enable_multi_pointer']: - atexit.register(cleanup_multi_pointer) + atexit.register(cleanup_multi_pointer, xinput_device_keyword) print("Create master input. . .") cmd = "xinput create-master 'HuionTablet'" @@ -304,12 +304,14 @@ def multi_pointer(xinput_device_keyword=XINPUT_DEVICE_KEYWORD): # ----------------------------------------------------------------------------- -def cleanup_multi_pointer(): +def cleanup_multi_pointer(xinput_device_keyword=XINPUT_DEVICE_KEYWORD): """ Cleanup multi pointer at program exit. """ + master = os.popen("xinput | grep 'HuionTablet pointer' | cut -f 2").read() + master = master.split('\n')[0][3:] # Removing existing master - cmd = "xinput --remove-master HuionTablet" + cmd = "xinput remove-master {}".format(master) if main.settings['debug_mode']: print('» {}'.format(cmd)) os.popen(cmd) @@ -856,3 +858,4 @@ def read_config(): # ----------------------------------------------------------------------------- if __name__ == '__main__': main.run() +