_ui.py 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. """
  2. rescriptoon
  3. Copyright (C) 2018-2019 Fabian Peter Hammerle <fabian@hammerle.me>
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. """
  15. import typing
  16. import Xlib.display
  17. def _get_system_tray(
  18. display: Xlib.display.Display,
  19. ) -> typing.Optional["Xlib.display.Window"]:
  20. tray: "Xlib.display.Window" = display.get_selection_owner(
  21. display.intern_atom("_NET_SYSTEM_TRAY_S{}".format(display.get_default_screen()))
  22. )
  23. if tray != Xlib.X.NONE:
  24. return tray
  25. return None
  26. class SystemTrayUnavailable(Exception):
  27. pass
  28. def _add_window_to_system_tray(
  29. display: Xlib.display.Display, window: "Xlib.display.Window"
  30. ) -> None:
  31. system_tray = _get_system_tray(display)
  32. if not system_tray:
  33. raise SystemTrayUnavailable()
  34. display.send_event(
  35. system_tray,
  36. Xlib.display.event.ClientMessage(
  37. client_type=display.intern_atom("_NET_SYSTEM_TRAY_OPCODE"),
  38. window=system_tray.id,
  39. data=(32, (Xlib.X.CurrentTime, 0, window.id, 0, 0,),),
  40. ),
  41. )
  42. class TrayIcon:
  43. def __init__(self, display: "Xlib.display.Display"):
  44. self._display = display
  45. screen = display.screen()
  46. self._window = screen.root.create_window(
  47. # x, y
  48. -1,
  49. -1,
  50. # width, height
  51. 1,
  52. 1,
  53. # border width
  54. 0,
  55. screen.root_depth,
  56. event_mask=Xlib.X.StructureNotifyMask,
  57. )
  58. self._window.set_wm_class("RescriptoonTrayIcon", "Rescriptoon")
  59. self._graphics_context = self._window.create_gc()
  60. colormap = screen.default_colormap
  61. self._color_enabled = colormap.alloc_named_color("green").pixel
  62. self._color_disabled = colormap.alloc_named_color("red").pixel
  63. _add_window_to_system_tray(display=display, window=self._window)
  64. def draw(self, enabled: bool) -> None:
  65. dim = self._window.get_geometry()
  66. self._graphics_context.change(
  67. foreground=self._color_enabled if enabled else self._color_disabled
  68. )
  69. self._window.fill_rectangle(self._graphics_context, 0, 0, dim.width, dim.height)