src/X11Command.h
branchv_0
changeset 3 72384bb5c66e
parent 2 8d44cba0a3d1
child 4 820c4a4a3ed8
equal deleted inserted replaced
2:8d44cba0a3d1 3:72384bb5c66e
    39 		// TODO: more OOP
    39 		// TODO: more OOP
    40 
    40 
    41 		virtual ~Display() {
    41 		virtual ~Display() {
    42 			if (display) XCloseDisplay(display);
    42 			if (display) XCloseDisplay(display);
    43 		}
    43 		}
    44 
       
    45 	};
    44 	};
    46 
    45 
    47 	class DeviceList {
    46 	class Device {
       
    47 	public:
       
    48 		XDevice* device = nullptr;
       
    49 		// TODO: more OOP
       
    50 
       
    51 		virtual ~Device() {
       
    52 			if (device) XFree(device);
       
    53 		}
       
    54 	};
       
    55 
       
    56 	class DeviceInfoList {
    48 	public:
    57 	public:
    49 		XDeviceInfo* items = nullptr;
    58 		XDeviceInfo* items = nullptr;
    50 		int count = 0;
    59 		int size = 0;
    51 		// TODO: more OOP
    60 		// TODO: more OOP
    52 
    61 
    53 		virtual ~DeviceList() {
    62 		XDeviceInfo* operator[](int index) const {
       
    63 			if (index < size) return items + index;
       
    64 			else throw std::out_of_range("invalid index of XDeviceInfo");
       
    65 		}
       
    66 
       
    67 		virtual ~DeviceInfoList() {
    54 			if (items) XFreeDeviceList(items);
    68 			if (items) XFreeDeviceList(items);
    55 		}
    69 		}
    56 
       
    57 	};
    70 	};
    58 
    71 
    59 
    72 
    60 	std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings and use platform encoding as default
    73 	std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // TODO: use platform encoding as default
    61 
    74 
    62 	relpipe::common::type::StringX getDeviceType(Display& display, XDeviceInfo* device) {
    75 	relpipe::common::type::StringX getDeviceType(const Display& display, const XDeviceInfo* device) {
    63 		if (device && device->type) {
    76 		if (device && device->type) {
    64 			char* raw = XGetAtomName(display.display, device->type);
    77 			char* raw = XGetAtomName(display.display, device->type);
    65 			if (raw) {
    78 			if (raw) {
    66 				relpipe::common::type::StringX type = convertor.from_bytes(raw);
    79 				relpipe::common::type::StringX type = convertor.from_bytes(raw);
    67 				XFree(raw);
    80 				XFree(raw);
    70 			}
    83 			}
    71 		}
    84 		}
    72 		return L"";
    85 		return L"";
    73 	}
    86 	}
    74 
    87 
    75 	void listInputDevices(Display& display, Configuration& configuration, std::shared_ptr<writer::RelationalWriter> writer) {
    88 	void listInputDevices(const Display& display, Configuration& configuration, std::shared_ptr<writer::RelationalWriter> writer, std::function<void() > relationalWriterFlush) {
    76 		writer->startRelation(L"x11_input_device",{
    89 		writer->startRelation(L"x11_input_device",{
    77 			{L"id", relpipe::writer::TypeId::INTEGER},
    90 			{L"id", relpipe::writer::TypeId::INTEGER},
    78 			{L"name", relpipe::writer::TypeId::STRING},
    91 			{L"name", relpipe::writer::TypeId::STRING},
    79 			{L"type", relpipe::writer::TypeId::STRING},
    92 			{L"type", relpipe::writer::TypeId::STRING},
    80 		}, true);
    93 		}, true);
    81 
    94 
    82 		DeviceList devices;
    95 		DeviceInfoList devices;
    83 		devices.items = XListInputDevices(display.display, &devices.count);
    96 		devices.items = XListInputDevices(display.display, &devices.size);
    84 
    97 
    85 		for (int i = 0; i < devices.count; i++) {
    98 		for (int i = 0; i < devices.size; i++) {
    86 			relpipe::common::type::Integer id = (devices.items + i)->id;
    99 			relpipe::common::type::Integer id = devices[i]->id;
    87 			relpipe::common::type::StringX name = convertor.from_bytes((devices.items + i)->name);
   100 			relpipe::common::type::StringX name = convertor.from_bytes(devices[i]->name);
    88 			relpipe::common::type::StringX type = getDeviceType(display, devices.items + i);
   101 			relpipe::common::type::StringX type = getDeviceType(display, devices[i]);
    89 
   102 
    90 			writer->writeAttribute(&id, typeid (id));
   103 			writer->writeAttribute(&id, typeid (id));
    91 			writer->writeAttribute(&name, typeid (name));
   104 			writer->writeAttribute(&name, typeid (name));
    92 			writer->writeAttribute(&type, typeid (type));
   105 			writer->writeAttribute(&type, typeid (type));
    93 		}
   106 		}
    94 	}
   107 
    95 
   108 		relationalWriterFlush();
    96 	void listInputEvents(Display& display, Configuration& configuration, std::shared_ptr<writer::RelationalWriter> writer) {
   109 	}
       
   110 
       
   111 	/**
       
   112 	 * Fields of this structure are filled in DeviceKeyPress(), DeviceButtonPress() … macros.
       
   113 	 */
       
   114 	class EventType {
       
   115 	private:
       
   116 		const static int UNUSED = -1;
       
   117 	public:
       
   118 		int motion = UNUSED;
       
   119 		int buttonPress = UNUSED;
       
   120 		int buttonRelease = UNUSED;
       
   121 		int keyPress = UNUSED;
       
   122 		int keyRelease = UNUSED;
       
   123 		int proximityIn = UNUSED;
       
   124 		int proximityOut = UNUSED;
       
   125 	} eventType;
       
   126 
       
   127 	void registerEvents(const Display& display, const XDeviceInfo* deviceInfo) {
       
   128 		bool registerProximityEvents = false; // TODO: proximity?
       
   129 
       
   130 		Device device;
       
   131 		Window window;
       
   132 		int screen;
       
   133 
       
   134 		int eventCount = 0;
       
   135 		XEventClass events[7]; // TODO: check array size
       
   136 		XInputClassInfo* classInfo;
       
   137 		int classIndex;
       
   138 
       
   139 		screen = DefaultScreen(display.display);
       
   140 		window = RootWindow(display.display, screen);
       
   141 		// TODO: configurable window from which we capture the events or optionally open our own window (can also provide some visual feedback/info)
       
   142 		// Currently we can do something like:
       
   143 		//   Xephyr  :5 -screen 1920x1080
       
   144 		//   DISPLAY=:5 relpipe-in-x11 --list-input-devices false --list-input-events true | …
       
   145 
       
   146 		device.device = XOpenDevice(display.display, deviceInfo->id);
       
   147 
       
   148 		if (device.device) {
       
   149 			if (device.device->num_classes > 0) {
       
   150 				for (classInfo = device.device->classes, classIndex = 0; classIndex < deviceInfo->num_classes; classInfo++, classIndex++) {
       
   151 					if (classInfo->input_class == KeyClass) {
       
   152 						DeviceKeyPress(device.device, eventType.keyPress, events[eventCount]);
       
   153 						eventCount++;
       
   154 						DeviceKeyRelease(device.device, eventType.keyRelease, events[eventCount]);
       
   155 						eventCount++;
       
   156 					} else if (classInfo->input_class == ButtonClass) {
       
   157 						DeviceButtonPress(device.device, eventType.buttonPress, events[eventCount]);
       
   158 						eventCount++;
       
   159 						DeviceButtonRelease(device.device, eventType.buttonRelease, events[eventCount]);
       
   160 						eventCount++;
       
   161 					} else if (classInfo->input_class == ValuatorClass) {
       
   162 						DeviceMotionNotify(device.device, eventType.motion, events[eventCount]);
       
   163 						eventCount++;
       
   164 						if (registerProximityEvents) {
       
   165 							ProximityIn(device.device, eventType.proximityIn, events[eventCount]);
       
   166 							eventCount++;
       
   167 							ProximityOut(device.device, eventType.proximityOut, events[eventCount]);
       
   168 							eventCount++;
       
   169 						}
       
   170 					}
       
   171 				}
       
   172 
       
   173 				int result = XSelectExtensionEvent(display.display, window, events, eventCount);
       
   174 				if (result != Success) throw std::logic_error("Unable to register events from device: " + std::to_string(deviceInfo->id) + " Result: " + std::to_string(result));
       
   175 			}
       
   176 		} else {
       
   177 			throw std::invalid_argument("Unable to open the device: " + std::to_string(deviceInfo->id));
       
   178 		}
       
   179 	}
       
   180 
       
   181 	void listInputEvents(Display& display, Configuration& configuration, std::shared_ptr<writer::RelationalWriter> writer, std::function<void() > relationalWriterFlush) {
    97 		writer->startRelation(L"x11_input_event",{
   182 		writer->startRelation(L"x11_input_event",{
    98 			{L"device_id", relpipe::writer::TypeId::INTEGER},
   183 			{L"device", relpipe::writer::TypeId::INTEGER},
       
   184 			{L"type", relpipe::writer::TypeId::STRING},
       
   185 			{L"state", relpipe::writer::TypeId::STRING},
       
   186 			{L"key", relpipe::writer::TypeId::INTEGER},
       
   187 			{L"button", relpipe::writer::TypeId::INTEGER},
       
   188 			{L"x", relpipe::writer::TypeId::INTEGER},
       
   189 			{L"y", relpipe::writer::TypeId::INTEGER},
       
   190 			// {L"x_root", relpipe::writer::TypeId::INTEGER},
       
   191 			// {L"y_root", relpipe::writer::TypeId::INTEGER},
    99 		}, true);
   192 		}, true);
   100 
   193 
   101 		// TODO: list events
   194 
   102 	}
   195 		{
       
   196 			DeviceInfoList devices;
       
   197 			devices.items = XListInputDevices(display.display, &devices.size);
       
   198 			for (int i = 0; i < devices.size; i++) {
       
   199 				try {
       
   200 					registerEvents(display, devices[i]);
       
   201 				} catch (...) {
       
   202 					// exception "Unable to open device: …"
       
   203 					// TODO: do not call registerEvents() for some devices, skip them
       
   204 				}
       
   205 			}
       
   206 		}
       
   207 
       
   208 		for (XEvent event; true;) {
       
   209 			XNextEvent(display.display, &event);
       
   210 
       
   211 			relpipe::common::type::Integer device = -1; // TODO: null
       
   212 			relpipe::common::type::StringX type;
       
   213 			relpipe::common::type::StringX state;
       
   214 			relpipe::common::type::Integer button = -1; // TODO: null
       
   215 			relpipe::common::type::Integer key = -1; // TODO: null
       
   216 			relpipe::common::type::Integer x = -1; // TODO: null
       
   217 			relpipe::common::type::Integer y = -1; // TODO: null
       
   218 			// relpipe::common::type::Integer xRoot = -1; // TODO: null
       
   219 			// relpipe::common::type::Integer yRoot = -1; // TODO: null
       
   220 
       
   221 			if (event.type == eventType.motion) {
       
   222 				XDeviceMotionEvent* e = (XDeviceMotionEvent*) & event;
       
   223 				device = e->deviceid;
       
   224 				type = L"motion";
       
   225 				x = e->x;
       
   226 				y = e->y;
       
   227 				// xRoot = e->x_root;
       
   228 				// yRoot = e->y_root;
       
   229 			} else if (event.type == eventType.buttonPress || event.type == eventType.buttonRelease) {
       
   230 				XDeviceButtonEvent* e = (XDeviceButtonEvent*) & event;
       
   231 				device = e->deviceid;
       
   232 				type = L"button";
       
   233 				state = event.type == eventType.buttonPress ? L"pressed" : L"released";
       
   234 				button = e->button;
       
   235 				x = e->x;
       
   236 				y = e->y;
       
   237 				// xRoot = e->x_root;
       
   238 				// yRoot = e->y_root;
       
   239 			} else if (event.type == eventType.keyPress || event.type == eventType.keyRelease) {
       
   240 				XDeviceKeyEvent* e = (XDeviceKeyEvent*) & event;
       
   241 				device = e->deviceid;
       
   242 				type = L"key";
       
   243 				state = event.type == eventType.keyPress ? L"pressed" : L"released";
       
   244 				key = e->keycode;
       
   245 				x = e->x;
       
   246 				y = e->y;
       
   247 				// xRoot = e->x_root;
       
   248 				// yRoot = e->y_root;
       
   249 			} else if (event.type == eventType.proximityIn || event.type == eventType.proximityOut) {
       
   250 				XProximityNotifyEvent* e = (XProximityNotifyEvent*) & event;
       
   251 				device = e->deviceid;
       
   252 				type = L"proximity";
       
   253 			}
       
   254 
       
   255 			writer->writeAttribute(&device, typeid (device));
       
   256 			writer->writeAttribute(type);
       
   257 			writer->writeAttribute(state);
       
   258 			writer->writeAttribute(&key, typeid (key));
       
   259 			writer->writeAttribute(&button, typeid (button));
       
   260 			writer->writeAttribute(&x, typeid (x));
       
   261 			writer->writeAttribute(&y, typeid (y));
       
   262 			// writer->writeAttribute(&xRoot, typeid (xRoot));
       
   263 			// writer->writeAttribute(&yRoot, typeid (yRoot));
       
   264 			relationalWriterFlush();
       
   265 		}
       
   266 	}
       
   267 
       
   268 	static int handleXError(::Display* display, XErrorEvent* errorEvent) {
       
   269 		// FIXME: print error
       
   270 		return 0;
       
   271 	}
       
   272 
   103 
   273 
   104 public:
   274 public:
   105 
   275 
   106 	void process(Configuration& configuration, std::shared_ptr<writer::RelationalWriter> writer) {
   276 	void process(Configuration& configuration, std::shared_ptr<writer::RelationalWriter> writer, std::function<void() > relationalWriterFlush) {
       
   277 		XSetErrorHandler(handleXError);
       
   278 
   107 		Display display;
   279 		Display display;
   108 		display.display = XOpenDisplay(nullptr);
   280 		display.display = XOpenDisplay(nullptr);
   109 
   281 
   110 		if (display.display) {
   282 		if (display.display) {
   111 			if (configuration.listInputDevices) listInputDevices(display, configuration, writer);
   283 			if (configuration.listInputDevices) listInputDevices(display, configuration, writer, relationalWriterFlush);
   112 			if (configuration.listInputEvents) listInputEvents(display, configuration, writer);
   284 			if (configuration.listInputEvents) listInputEvents(display, configuration, writer, relationalWriterFlush);
   113 		} else {
   285 		} else {
   114 			throw std::invalid_argument("Unable to open display. Please check the $DISPLAY variable.");
   286 			throw std::invalid_argument("Unable to open display. Please check the $DISPLAY variable.");
   115 		}
   287 		}
   116 	}
   288 	}
   117 };
   289 };