1 // 2 // Copyright (c) 2010-2018 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using System.Runtime.InteropServices; 10 using System.Threading; 11 using System.Diagnostics; 12 13 namespace Antmicro.Renode.Backends.Display.XInput 14 { 15 public static class XLibHelper 16 { XLibHelper()17 static XLibHelper() 18 { 19 errorHandler = ((display, error) => 20 { 21 var x11e = X11Error; 22 if(x11e != null) 23 { 24 x11e(); 25 } 26 return 0; 27 }); 28 29 var fpointer = Marshal.GetFunctionPointerForDelegate(errorHandler); 30 XSetErrorHandler(fpointer); 31 } 32 GrabCursorByWindow(int windowId)33 public static void GrabCursorByWindow(int windowId) 34 { 35 lock(locker) 36 { 37 using(var l = new XLibLocker(DisplayHandle)) 38 { 39 int res = 1; 40 do 41 { 42 // Not always grabbing mouse works at the first time, so the loop is necessary 43 res = XGrabPointer(DisplayHandle, windowId, true, 0x4 | 0x8 | 0x40, 1, 1, windowId, DefineEmptyCursor(windowId), 0); 44 Thread.Sleep(100); 45 } 46 while(res != 0); 47 48 XGrabKeyboard(DisplayHandle, windowId, true, 1, 1, 0); 49 } 50 } 51 } 52 UngrabCursor()53 public static void UngrabCursor() 54 { 55 lock(locker) 56 { 57 using(var l = new XLibLocker(DisplayHandle)) 58 { 59 XUngrabPointer(DisplayHandle, 0); 60 XUngrabKeyboard(DisplayHandle, 0); 61 } 62 63 XCloseDisplay(DisplayHandle); 64 DisplayHandle = XOpenDisplay(IntPtr.Zero); 65 } 66 } 67 GetCursorPosition(int win)68 public static Tuple<int, int> GetCursorPosition(int win) 69 { 70 IntPtr r = IntPtr.Zero, c = IntPtr.Zero; 71 int rx = -1, ry = -1, wx = -1, wy = -1; 72 uint m = 0; 73 74 using(var l = new XLibLocker(DisplayHandle)) 75 { 76 XQueryPointer(DisplayHandle, win, ref r, ref c, ref rx, ref ry, ref wx, ref wy, ref m); 77 } 78 return Tuple.Create(rx, ry); 79 } 80 MoveCursorRelative(int dx, int dy)81 public static void MoveCursorRelative(int dx, int dy) 82 { 83 XWarpPointer(DisplayHandle, 0, 0, 0, 0, 0, 0, dx, dy); 84 } 85 MoveCursorAbsolute(int windowId, int x, int y)86 public static void MoveCursorAbsolute(int windowId, int x, int y) 87 { 88 XWarpPointer(DisplayHandle, 0, windowId, 0, 0, 0, 0, x, y); 89 } 90 StartEventListenerLoop(IInputHandler handler)91 public static void StartEventListenerLoop(IInputHandler handler) 92 { 93 lock(EventListenerThread) 94 { 95 if(!EventListenerThread.IsAlive) 96 { 97 EventListenerThread.Name = "XLib event listener"; 98 EventListenerThread.IsBackground = true; 99 EventListenerThread.Start(handler); 100 } 101 } 102 } 103 104 public static bool IsAvailable 105 { 106 get 107 { 108 try 109 { 110 XOpenDisplay(IntPtr.Zero); 111 return true; 112 } 113 catch(Exception) 114 { 115 } 116 117 return false; 118 } 119 } 120 121 public static Action X11Error; 122 123 [DllImport("libX11", EntryPoint = "XChangeActivePointerGrab")] XChangeActivePointerGrab(IntPtr display, uint eventMask, IntPtr cursor, int time)124 internal extern static int XChangeActivePointerGrab(IntPtr display, uint eventMask, IntPtr cursor, /*Time*/int time); 125 126 [DllImport("libX11", EntryPoint = "XGrabPointer")] XGrabPointer(IntPtr display, int grabWindow, bool ownerEvents, uint eventMask, int pointerMode, int keyboardMode, int confineTo, IntPtr cursor, int time)127 internal extern static int XGrabPointer(IntPtr display, int grabWindow, bool ownerEvents, uint eventMask, int pointerMode, int keyboardMode, int confineTo, IntPtr cursor, /*Time*/int time); 128 129 [DllImport("libX11", EntryPoint = "XUngrabPointer")] XUngrabPointer(IntPtr display, int time)130 internal extern static int XUngrabPointer(IntPtr display, int time); 131 132 [DllImport("libX11", EntryPoint = "XGrabKeyboard")] XGrabKeyboard(IntPtr display, int grabWindow, bool ownerEvents, int pointerMode, int keyboardMode, int time)133 internal extern static int XGrabKeyboard(IntPtr display, int grabWindow, bool ownerEvents, int pointerMode, int keyboardMode, int time); 134 135 [DllImport("libX11", EntryPoint = "XUngrabKeyboard")] XUngrabKeyboard(IntPtr display, int time)136 internal extern static int XUngrabKeyboard(IntPtr display, int time); 137 138 [DllImport("libX11", EntryPoint = "XCreatePixmapFromBitmapData")] XCreatePixmapFromBitmapData(IntPtr display, int drawable, byte[] data, int width, int height, IntPtr fg, IntPtr bg, int depth)139 internal extern static IntPtr XCreatePixmapFromBitmapData(IntPtr display, int drawable, byte[] data, int width, int height, IntPtr fg, IntPtr bg, int depth); 140 141 [DllImport("libX11", EntryPoint = "XFreePixmap")] XFreePixmap(IntPtr display, IntPtr pixmap)142 internal extern static IntPtr XFreePixmap(IntPtr display, IntPtr pixmap); 143 144 [DllImport("libX11", EntryPoint = "XCreatePixmapCursor")] XCreatePixmapCursor(IntPtr display, IntPtr source, IntPtr mask, ref IntPtr foreground_color, ref IntPtr background_color, int x_hot, int y_hot)145 internal extern static IntPtr XCreatePixmapCursor(IntPtr display, IntPtr source, IntPtr mask, ref IntPtr foreground_color, ref IntPtr background_color, int x_hot, int y_hot); 146 147 [DllImport("libX11", EntryPoint = "XWhitePixel")] XWhitePixel(IntPtr display, int screen_no)148 internal extern static IntPtr XWhitePixel(IntPtr display, int screen_no); 149 150 [DllImport("libX11", EntryPoint = "XBlackPixel")] XBlackPixel(IntPtr display, int screen_no)151 internal extern static IntPtr XBlackPixel(IntPtr display, int screen_no); 152 153 [DllImport("libX11", EntryPoint = "XRootWindow")] XRootWindow(IntPtr display, int screen_number)154 internal extern static int XRootWindow(IntPtr display, int screen_number); 155 156 [DllImport("libX11", EntryPoint = "XDefaultScreen")] XDefaultScreen(IntPtr display)157 internal extern static int XDefaultScreen(IntPtr display); 158 159 [DllImport("libX11", EntryPoint = "XOpenDisplay")] XOpenDisplay(IntPtr display)160 internal extern static IntPtr XOpenDisplay(IntPtr display); 161 162 [DllImport("libX11", EntryPoint = "XCloseDisplay")] XCloseDisplay(IntPtr display)163 internal extern static int XCloseDisplay(IntPtr display); 164 165 [DllImport("libX11", EntryPoint = "XWarpPointer")] XWarpPointer(IntPtr display, int srcWindowId, int dstWindowId, int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY)166 internal extern static void XWarpPointer(IntPtr display, int srcWindowId, int dstWindowId, int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY); 167 168 [DllImport("libX11", EntryPoint = "XQueryPointer")] XQueryPointer(IntPtr display, int window, ref IntPtr root, ref IntPtr child, ref int rootX, ref int rootY, ref int winX, ref int winY, ref uint mask)169 internal extern static void XQueryPointer(IntPtr display, int window, ref IntPtr root, ref IntPtr child, ref int rootX, ref int rootY, ref int winX, ref int winY, ref uint mask); 170 171 [DllImport("libX11", EntryPoint = "XNextEvent")] XNextEvent(IntPtr display, IntPtr e)172 internal extern static void XNextEvent(IntPtr display, IntPtr e); 173 174 [DllImport("libX11", EntryPoint = "XLockDisplay")] XLockDisplay(IntPtr display)175 internal extern static void XLockDisplay(IntPtr display); 176 177 [DllImport("libX11", EntryPoint = "XUnlockDisplay")] XUnlockDisplay(IntPtr display)178 internal extern static void XUnlockDisplay(IntPtr display); 179 180 [DllImport("libX11", EntryPoint = "XSetErrorHandler")] XSetErrorHandler(IntPtr handler)181 internal extern static int XSetErrorHandler(IntPtr handler); 182 183 [Conditional("DEBUG")] DebugPrint(string str, params object[] p)184 private static void DebugPrint(string str, params object[] p) 185 { 186 Console.WriteLine(str, p); 187 } 188 DefineEmptyCursor(int windowId)189 private static IntPtr DefineEmptyCursor(int windowId) 190 { 191 var c = Marshal.AllocHGlobal(80); 192 var cursor_bits = new byte[2 * 16]; 193 IntPtr cursor; 194 195 using(var l = new XLibLocker(DisplayHandle)) 196 { 197 var pixmap = XCreatePixmapFromBitmapData(DisplayHandle, windowId, cursor_bits, 16, 16, (IntPtr)1, (IntPtr)0, 1); 198 cursor = XCreatePixmapCursor(DisplayHandle, pixmap, pixmap, ref c, ref c, 0, 0); 199 XFreePixmap(DisplayHandle, pixmap); 200 } 201 Marshal.FreeHGlobal(c); 202 203 return cursor; 204 } 205 EventListenerLoop(object h)206 private static void EventListenerLoop(object h) 207 { 208 var handler = h as IInputHandler; 209 if(handler == null) 210 { 211 return; 212 } 213 214 int initMouseX = -1; 215 int initMouseY = -1; 216 217 using(var ev = new XEvent()) 218 { 219 handler.Stop = false; 220 while(!handler.Stop) 221 { 222 XNextEvent(DisplayHandle, ev.Pointer); 223 var type = ev.Type; 224 switch(type) 225 { 226 /* 227 * Mouse cursor moved 228 */ 229 case XEvent.MotionNotify: 230 231 var rx = ev.MotionNotifyXRoot; 232 var ry = ev.MotionNotifyYRoot; 233 234 if(initMouseX == -1 && initMouseY == -1) 235 { 236 initMouseX = rx; 237 initMouseY = ry; 238 continue; 239 } 240 241 var dx = rx - initMouseX; 242 var dy = ry - initMouseY; 243 244 if(!(dx == 0 && dy == 0)) 245 { 246 handler.MouseMoved(rx, ry, dx, dy); 247 248 if(handler.CursorFixed) 249 { 250 MoveCursorAbsolute(XRootWindow(DisplayHandle, XDefaultScreen(DisplayHandle)), initMouseX, initMouseY); 251 } 252 } 253 break; 254 /* 255 * Mouse button pressed 256 */ 257 case XEvent.ButtonPress: 258 handler.ButtonPressed(ev.Button); 259 break; 260 /* 261 * Mouse button released 262 */ 263 case XEvent.ButtonRelease: 264 handler.ButtonReleased(ev.Button); 265 break; 266 /* 267 * Keyboard key pressed 268 */ 269 case XEvent.KeyPress: 270 handler.KeyPressed(ev.KeyCode); 271 break; 272 /* 273 * Keyboard key released 274 */ 275 case XEvent.KeyRelease: 276 handler.KeyReleased(ev.KeyCode); 277 break; 278 } 279 } 280 } 281 282 EventListenerThread = new Thread(EventListenerLoop); 283 } 284 285 private static Thread EventListenerThread = new Thread(EventListenerLoop); 286 private static IntPtr DisplayHandle = XOpenDisplay(IntPtr.Zero); 287 private static Func<IntPtr, IntPtr, int> errorHandler; 288 private static object locker = new object(); 289 290 // inspired by OpenTK solution 291 private class XLibLocker : IDisposable 292 { XLibLocker(IntPtr display)293 public XLibLocker(IntPtr display) 294 { 295 this.display = display; 296 XLockDisplay(display); 297 } 298 Dispose()299 public void Dispose() 300 { 301 XUnlockDisplay(display); 302 } 303 304 private readonly IntPtr display; 305 } 306 307 private class XEvent : IDisposable 308 { XEvent()309 public XEvent() 310 { 311 Pointer = Marshal.AllocCoTaskMem(296); 312 } 313 Dispose()314 public void Dispose() 315 { 316 Marshal.FreeCoTaskMem(Pointer); 317 } 318 319 public IntPtr Pointer { get; private set; } 320 321 public int Type 322 { 323 get { return Marshal.ReadInt32(Pointer, 0x0); } 324 } 325 326 public long Time 327 { 328 get { return Marshal.ReadInt64(Pointer, 0x38); } 329 } 330 331 public int MotionNotifyXRoot 332 { 333 get { return Marshal.ReadInt32(Pointer, 0x48); } 334 } 335 336 public int MotionNotifyYRoot 337 { 338 get { return Marshal.ReadInt32(Pointer, 0x4c); } 339 } 340 341 public int Button 342 { 343 get { return Marshal.ReadInt32(Pointer, 0x54); } 344 } 345 346 public int KeyCode 347 { 348 get 349 { 350 // yes, it's the same as Button! 351 return Marshal.ReadInt32(Pointer, 0x54); 352 } 353 } 354 355 public const int KeyPress = 0x2; 356 public const int KeyRelease = 0x3; 357 public const int ButtonPress = 0x4; 358 public const int ButtonRelease = 0x5; 359 public const int MotionNotify = 0x6; 360 } 361 } 362 } 363 364