X-Git-Url: https://adrianiainlam.tk/git/?a=blobdiff_plain;f=indicator-keyboard-led.py;h=9213a8334336e323230c5a761144795f715403bf;hb=cd02f43e3b3cc2b64edcca9651232ed674cd0bd1;hp=44e02a0ba096ac4beb3e60a0a328773feaf8b03f;hpb=fc1c079170ce7a35bd5c097f6d9fde70ba575be4;p=indicator-keyboard-led.git diff --git a/indicator-keyboard-led.py b/indicator-keyboard-led.py index 44e02a0..9213a83 100755 --- a/indicator-keyboard-led.py +++ b/indicator-keyboard-led.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # # indicator-keyboard-led - simulate keyboard lock keys LED -# Copyright (c) 2016 Adrian I Lam +# Copyright (c) 2017 Adrian I Lam # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), @@ -28,69 +28,118 @@ import signal import subprocess -from os import path +import os import argparse +import shutil import gi gi.require_version('Gdk', '3.0') gi.require_version('Gtk', '3.0') gi.require_version('AppIndicator3', '0.1') from gi.repository import Gdk, Gtk, AppIndicator3 +APP_NAME = 'indicator-keyboard-led' +APP_VERSION = '1.1' + +ICON_LOCATION = '/usr/share/icons/hicolor/scalable/apps/' + APP_NAME + '.svg' +import gettext +t = gettext.translation(APP_NAME, '/usr/share/locale') +_ = t.gettext + class IndicatorKeyboardLED: - locks = { 'caps': 'Caps', 'num': 'Num', 'scr': 'Scroll' } - - def __init__(self, short=False): + def __init__(self, short=False, order='NCS', xdotool=None): + self.validate_order(order) + self.indicator = AppIndicator3.Indicator.new( - 'indicator-keyboard-led', - path.join(path.dirname(path.realpath(__file__)), 'icon.svg'), + APP_NAME, ICON_LOCATION, AppIndicator3.IndicatorCategory.APPLICATION_STATUS) self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE) - + if short: - self.locks = { 'caps': 'C', 'num': 'N', 'scr': 'S' } + self.locks = { 'N': _('N'), 'C': _('C'), 'S': _('S') } + else: + self.locks = { 'N': _('Num'), 'C': _('Caps'), 'S': _('Scroll') } keymap = Gdk.Keymap.get_default() - keymap.connect('state-changed', self.update_indicator) - self.update_indicator(keymap) + keymap.connect('state-changed', self.update_indicator, order) + self.update_indicator(keymap, order) + self.create_menu(xdotool, order) + def create_menu(self, xdotool, order): menu = Gtk.Menu() - items = { - 'caps' : Gtk.MenuItem.new_with_label('Caps Lock'), - 'num' : Gtk.MenuItem.new_with_label('Num Lock'), - 'scr' : Gtk.MenuItem.new_with_label('Scroll Lock') - } - menu.append(items['caps']) - menu.append(items['num']) - menu.append(items['scr']) - - items['caps'].connect('activate', self.send_keypress, 'Caps_Lock') - items['num' ].connect('activate', self.send_keypress, 'Num_Lock') - items['scr' ].connect('activate', self.send_keypress, 'Scroll_Lock') + xdotool = xdotool or shutil.which('xdotool') + if xdotool and os.access(xdotool, os.X_OK) and os.path.isfile(xdotool): + def send_keypress(menuitem, keystroke): + subprocess.call([xdotool, 'key', keystroke]) + def new_menu_item(itemtype): + if itemtype == 'N': + item = Gtk.MenuItem.new_with_label(_('Num Lock')) + item.connect('activate', send_keypress, 'Num_Lock') + elif itemtype == 'C': + item = Gtk.MenuItem.new_with_label(_('Caps Lock')) + item.connect('activate', send_keypress, 'Caps_Lock') + elif itemtype == 'S': + item = Gtk.MenuItem.new_with_label(_('Scroll Lock')) + item.connect('activate', send_keypress, 'Scroll_Lock') + else: + raise ValueError('Invalid itemtype') + return item + + for i in order: + menu.append(new_menu_item(i)) + menu.append(Gtk.SeparatorMenuItem()) + + quit_item = Gtk.MenuItem.new_with_label(_('Quit')) + menu.append(quit_item) + quit_item.connect('activate', Gtk.main_quit) self.indicator.set_menu(menu) menu.show_all() - def update_indicator(self, keymap): - label = '⚫' if keymap.get_caps_lock_state() else '⚪' - label += self.locks['caps'] + ' ' - label += '⚫' if keymap.get_num_lock_state() else '⚪' - label += self.locks['num'] + ' ' - label += '⚫' if keymap.get_scroll_lock_state() else '⚪' - label += self.locks['scr'] - self.indicator.set_label(label, '') + def update_indicator(self, keymap, order): + labels = [] + for i in order: + if i == 'N': + state = keymap.get_num_lock_state() + elif i == 'C': + state = keymap.get_caps_lock_state() + elif i == 'S': + state = keymap.get_scroll_lock_state() + else: + raise ValueError('Invalid value in ORDER') + labels += [('⚫' if state else '⚪') + self.locks[i]] + self.indicator.set_label(' '.join(labels), '') + + def validate_order(self, order): + order = order.upper() + for i in order: + if i not in ['N', 'C', 'S']: + raise ValueError('Illegal character in ORDER. ' + '(Choices: [N, C, S])') + if len(order) != len(set(order)): + raise ValueError('Repeated character in ORDER. ' + 'Please specify each lock at most once.') + if 'S' in order and Gtk.check_version(3, 18, 0) is not None: + # Silently drop Scroll lock if GTK <= 3.18 + order.remove('S') - def send_keypress(self, menuitem, keystroke): - subprocess.call(['xdotool', 'key', keystroke]) if __name__ == '__main__': signal.signal(signal.SIGINT, lambda signum, frame: Gtk.main_quit()) - + parser = argparse.ArgumentParser( - description='indicator-keyboard-led - simulate keyboard lock keys LED') - parser.add_argument('-s', '--short', dest='short', action='store_true', - help='use short label, i.e. ⚫C ⚫N ⚫S instead of ⚫Caps ⚫Num ⚫Scroll', - required=False) + description='indicator-keyboard-led - simulate keyboard lock keys LED', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('-s', '--short', required=False, action='store_true', + help='use short label, i.e. ⚫N ⚫C ⚫S instead of ⚫Num ⚫Caps ⚫Scroll') + parser.add_argument('-o', '--order', required=False, default='NCS', + help='specify the order of the locks displayed, e.g. CSN for ' + '⚫Caps ⚫Scroll ⚫Num, or NC for ⚫Num ⚫Caps without Scroll lock') + parser.add_argument('-x', '--xdotool', required=False, + help='provide full path to xdotool executable, ' + 'e.g. "/usr/bin/xdotool"; ' + 'if not specified, search in PATH') args = parser.parse_args() - - IndicatorKeyboardLED(short=args.short) + + IndicatorKeyboardLED(short=args.short, order=args.order, + xdotool=args.xdotool) Gtk.main()