Lock a mouse cursor to a single monitor for multi-seat setups. https://aweirdimagination.net/2019/06/02/lightweight-multiseat-x/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

monitor-lock.py 4.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. #!/usr/bin/env python3
  2. import sys
  3. import os
  4. import time
  5. # Change path so we find Xlib
  6. sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
  7. from Xlib.display import Display
  8. from Xlib.ext import xinerama, xinput, xfixes
  9. from Xlib.protocol import rq
  10. xfixes_extname = 'XFIXES'
  11. BarrierPositiveX = (1 << 0)
  12. BarrierPositiveY = (1 << 1)
  13. BarrierNegativeX = (1 << 2)
  14. BarrierNegativeY = (1 << 3)
  15. class CreatePointerBarrier(rq.Request):
  16. _request = rq.Struct(rq.Card8('opcode'),
  17. rq.Opcode(31),
  18. rq.RequestLength(),
  19. rq.Resource('barrier'),
  20. rq.Window('window'),
  21. rq.Int16('x1'),
  22. rq.Int16('y1'),
  23. rq.Int16('x2'),
  24. rq.Int16('y2'),
  25. rq.Card32('directions'),
  26. rq.Pad(2),
  27. rq.LengthOf('devices', 2),
  28. rq.List('devices', xinput.DEVICEID),
  29. )
  30. def create_pointer_barrier(self, x1, y1, x2, y2, barrier_directions, devices=None):
  31. barrier = self.display.allocate_resource_id()
  32. CreatePointerBarrier(
  33. display=self.display,
  34. opcode=self.display.get_extension_major(xfixes_extname),
  35. window=self,
  36. x1=x1,
  37. y1=y1,
  38. x2=x2,
  39. y2=y2,
  40. directions=barrier_directions,
  41. devices=[] if not devices else devices,
  42. barrier=barrier)
  43. return barrier
  44. def main(argv):
  45. display = Display()
  46. screen = display.screen()
  47. window = screen.root
  48. def check_extension(name):
  49. if not display.has_extension(name):
  50. sys.stderr.write('%s: server does not have the %s extension\n' % (sys.argv[0], name))
  51. ext = display.query_extension(name)
  52. print(ext)
  53. sys.stderr.write("\n".join(display.list_extensions()))
  54. if ext is None:
  55. sys.exit(1)
  56. check_extension('XFIXES')
  57. check_extension('XInputExtension')
  58. check_extension('XINERAMA')
  59. xfixes_version = xfixes.QueryVersion(display=window.display, opcode=window.display.get_extension_major(xfixes_extname), major_version=5, minor_version=0)
  60. print('Found XFIXES version %s.%s' % (
  61. xfixes_version.major_version,
  62. xfixes_version.minor_version,
  63. ), file=sys.stderr)
  64. xinput_version = display.xinput_query_version()
  65. print('Found XInputExtension version %s.%s' % (
  66. xinput_version.major_version,
  67. xinput_version.minor_version,
  68. ), file=sys.stderr)
  69. xinerama_version = display.xinerama_query_version()
  70. print('Found XINERAMA version %s.%s' % (
  71. xinerama_version.major_version,
  72. xinerama_version.minor_version,
  73. ), file=sys.stderr)
  74. print()
  75. print('Available screens:')
  76. screens = xinerama.query_screens(window).screens
  77. for (idx, screen) in enumerate(screens):
  78. x, y, w, h = screen.x, screen.y, screen.width, screen.height
  79. print('screen %d: %s' % (idx, screen))
  80. print()
  81. print('Available pointers:')
  82. pointer_name = {}
  83. devices = xinput.query_device(window, xinput.AllMasterDevices).devices
  84. for device in devices:
  85. if 'pointer' in device.name:
  86. pointer_name[device.deviceid] = 'device %d: %s' % (device.deviceid, device.name)
  87. print(pointer_name[device.deviceid])
  88. if len(sys.argv) != 3:
  89. print()
  90. print('USAGE: %s [device] [screen]' % (sys.argv[0]))
  91. return -2
  92. lock_device = int(sys.argv[1])
  93. lock_screen = int(sys.argv[2])
  94. screen = screens[lock_screen]
  95. print()
  96. print('Creating barrier to force pointer %s to screen %d (%s)...' %\
  97. (pointer_name[lock_device], lock_screen, screens[lock_screen]))
  98. top = screen.y
  99. bottom = screen.y + screen.height
  100. left = screen.x + 1
  101. right = screen.x + screen.width - 1
  102. print('Barrier bounds: left=%d, top=%d, right=%d, bottom=%d' % (left, top, right, bottom))
  103. # top
  104. create_pointer_barrier(window, left, top, right, top, BarrierPositiveY, [lock_device])
  105. # left
  106. create_pointer_barrier(window, left, top, left, bottom, BarrierPositiveX, [lock_device])
  107. # bottom
  108. create_pointer_barrier(window, left, bottom, right, bottom, BarrierNegativeY, [lock_device])
  109. # right
  110. create_pointer_barrier(window, right, top, right, bottom, BarrierNegativeX, [lock_device])
  111. display.sync()
  112. print('Created barrier.')
  113. input()
  114. if __name__ == '__main__':
  115. sys.exit(main(sys.argv))