# -*- coding: utf-8 -*-
#
# indicator-keyboard-led - simulate keyboard lock keys LED
-# Copyright (c) 2017 Adrian I Lam <me@adrianiainlam.tk>
+# Copyright (c) 2017-2023 Adrian I Lam <spam@adrianiainlam.tk> s/spam/me/
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
-#
+#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
-#
+#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
from gi.repository import Gdk, Gtk, AppIndicator3
APP_NAME = 'indicator-keyboard-led'
-APP_VERSION = '1.1'
+APP_VERSION = '1.2'
-ICON_LOCATION = '/usr/share/icons/hicolor/scalable/apps/' + APP_NAME + '.svg'
+ICON_FOLDER = '/usr/share/icons/hicolor/scalable/apps/'
import gettext
t = gettext.translation(APP_NAME, '/usr/share/locale')
_ = t.gettext
class IndicatorKeyboardLED:
- def __init__(self, short=False, order='NCS', xdotool=None):
+ def __init__(self, order='NCS', xdotool=None):
self.validate_order(order)
- self.indicator = AppIndicator3.Indicator.new(
- APP_NAME, ICON_LOCATION,
- AppIndicator3.IndicatorCategory.APPLICATION_STATUS)
- self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
+ self.indicators = {}
- if short:
- self.locks = { 'N': _('N'), 'C': _('C'), 'S': _('S') }
- else:
- self.locks = { 'N': _('Num'), 'C': _('Caps'), 'S': _('Scroll') }
+ count = 0
+ for i in order:
+ self.indicators[i] = AppIndicator3.Indicator.new(
+ '-'.join([APP_NAME, str(count), i]),
+ os.path.join(ICON_FOLDER, '-'.join([APP_NAME, i, 'off.svg'])),
+ AppIndicator3.IndicatorCategory.APPLICATION_STATUS)
+ self.indicators[i].set_status(AppIndicator3.IndicatorStatus.ACTIVE)
+
+ count += 1
keymap = Gdk.Keymap.get_default()
keymap.connect('state-changed', self.update_indicator, order)
self.create_menu(xdotool, order)
def create_menu(self, xdotool, order):
- menu = Gtk.Menu()
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:
+ 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 = Gtk.Menu()
+
+ if xdotool and os.access(xdotool, os.X_OK) and os.path.isfile(xdotool):
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)
+ 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()
+ self.indicators[i].set_menu(menu)
+ menu.show_all()
def update_indicator(self, keymap, order):
- labels = []
for i in order:
if i == 'N':
state = keymap.get_num_lock_state()
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), '')
+
+ icon = os.path.join(ICON_FOLDER, '-'.join([APP_NAME, i, 'on.svg' if state else 'off.svg']))
+ self.indicators[i].set_icon(icon)
+
def validate_order(self, order):
order = order.upper()
parser = argparse.ArgumentParser(
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')
+ '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, order=args.order,
+ IndicatorKeyboardLED(order=args.order,
xdotool=args.xdotool)
Gtk.main()