Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A few enhancements #53

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- [Config Examples](#config-examples-)
- [Multi-Monitor](#multi-monitor-)
- [Shortcuts](#shortcuts-)
- [Ubuntu Example for Huion Kamvas Pro 12"](#ubuntu-example-for-gt-116-)
- [Help Welcomed](#help-welcomed-)
- [To do](#to-do-)

Expand Down Expand Up @@ -163,6 +164,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 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.
```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. 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")

Expand Down
23 changes: 23 additions & 0 deletions TabletDriver
Original file line number Diff line number Diff line change
@@ -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
50 changes: 44 additions & 6 deletions config.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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]


#
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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]
Expand Down
122 changes: 117 additions & 5 deletions huion-tablet-driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@
import numexpr
from configparser import ConfigParser, ExtendedInterpolation
from time import gmtime, strftime, sleep
import atexit

MENU = {}
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


# -----------------------------------------------------------------------------
Expand All @@ -32,6 +36,8 @@ def run():
setup_driver()
calibrate()
multi_monitor()
calibrate_mapping()
multi_pointer()
main_loop()


Expand Down Expand Up @@ -180,6 +186,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 >")
Expand Down Expand Up @@ -259,6 +270,91 @@ 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):
"""
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']:
return

print("\nSetting up multiple pointers. . . ")

if main.settings['enable_multi_pointer']:
atexit.register(cleanup_multi_pointer, xinput_device_keyword)

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 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 {}".format(master)
if main.settings['debug_mode']:
print('» {}'.format(cmd))
os.popen(cmd)


# -----------------------------------------------------------------------------
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()
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'))) # lists all active monitors
xoutput_device = xoutput_devices[xoutput_device_argument]

# 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():

Expand Down Expand Up @@ -384,7 +480,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

Expand Down Expand Up @@ -495,7 +594,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)


# -----------------------------------------------------------------------------
Expand All @@ -520,7 +619,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)


# -----------------------------------------------------------------------------
Expand All @@ -541,9 +640,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)
Expand Down Expand Up @@ -683,6 +788,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:
Expand Down Expand Up @@ -753,3 +864,4 @@ def read_config():
# -----------------------------------------------------------------------------
if __name__ == '__main__':
main.run()