void QXcbConnection::xi2SetupTabletDevices() { Display *xDisplay = static_cast<Display *>(m_xlib_display); m_tabletData.clear(); int deviceCount = 0; XIDeviceInfo *devices = XIQueryDevice(xDisplay, XIAllDevices, &deviceCount); if (devices) { for (int i = 0; i < deviceCount; ++i) { int unused = 0; XIDeviceInfo *dev = XIQueryDevice(xDisplay, devices[i].deviceid, &unused); if (dev) { if (q_xi2_is_tablet(dev)) { TabletData tabletData; xi2QueryTabletData(dev, &tabletData); #ifdef XI2_TOUCH_DEBUG qDebug() << "found tablet" << dev->name; #endif m_tabletData.append(tabletData); } XIFreeDeviceInfo(dev); } } XIFreeDeviceInfo(devices); } }
static void uninstall_grabs_mouse( void ) { int i; int num_devices; XIDeviceInfo *info; assert( x11display.dpy && x11display.win ); if( !mouse_active ) return; XUndefineCursor(x11display.dpy, x11display.win); info = XIQueryDevice(x11display.dpy, XIAllDevices, &num_devices); for(i = 0; i < num_devices; i++) { if(info[i].use == XIFloatingSlave) { XIUngrabDevice(x11display.dpy, info[i].deviceid, CurrentTime); } else if(info[i].use == XIMasterPointer) { XIWarpPointer(x11display.dpy, info[i].deviceid, None, x11display.win, 0, 0, 0, 0, x11display.win_width/2, x11display.win_height/2); } } XIFreeDeviceInfo(info); mouse_active = qfalse; mx = my = 0; }
// Apply the event mask to the device and all its slaves. Only used in the // constructor. Remember, each KeyboardMouse has its own copy of the event // stream, which is how multiple event masks can "coexist." void KeyboardMouse::SelectEventsForDevice(Window window, XIEventMask *mask, int deviceid) { // Set the event mask for the master device. mask->deviceid = deviceid; XISelectEvents(m_display, window, mask, 1); // Query all the master device's slaves and set the same event mask for // those too. There are two reasons we want to do this. For mouse devices, // we want the raw motion events, and only slaves (i.e. physical hardware // devices) emit those. For keyboard devices, selecting slaves avoids // dealing with key focus. XIDeviceInfo* all_slaves; XIDeviceInfo* current_slave; int num_slaves; all_slaves = XIQueryDevice(m_display, XIAllDevices, &num_slaves); for (int i = 0; i < num_slaves; i++) { current_slave = &all_slaves[i]; if ((current_slave->use != XISlavePointer && current_slave->use != XISlaveKeyboard) || current_slave->attachment != deviceid) continue; mask->deviceid = current_slave->deviceid; XISelectEvents(m_display, window, mask, 1); } XIFreeDeviceInfo(all_slaves); }
int e_numdevices() { Display *display = XOpenDisplay(NULL); int ndevices; XIQueryDevice(display, XIAllDevices, &ndevices); return ndevices; }
XIDeviceInfo e_list(int i) { Display *display; display = XOpenDisplay(NULL); int ndevices; XIDeviceInfo *info = XIQueryDevice(display, XIAllDevices, &ndevices); return *(&info[i]); }
KeyboardMouse::KeyboardMouse(Window window, int opcode, int pointer, int keyboard) : m_window(window), xi_opcode(opcode), pointer_deviceid(pointer), keyboard_deviceid(keyboard) { memset(&m_state, 0, sizeof(m_state)); // The cool thing about each KeyboardMouse object having its own Display // is that each one gets its own separate copy of the X11 event stream, // which it can individually filter to get just the events it's interested // in. So be aware that each KeyboardMouse object actually has its own X11 // "context." m_display = XOpenDisplay(nullptr); int min_keycode, max_keycode; XDisplayKeycodes(m_display, &min_keycode, &max_keycode); int unused; // should always be 1 XIDeviceInfo* pointer_device = XIQueryDevice(m_display, pointer_deviceid, &unused); name = std::string(pointer_device->name); XIFreeDeviceInfo(pointer_device); XIEventMask mask; unsigned char mask_buf[(XI_LASTEVENT + 7)/8]; mask.mask_len = sizeof(mask_buf); mask.mask = mask_buf; memset(mask_buf, 0, sizeof(mask_buf)); XISetMask(mask_buf, XI_ButtonPress); XISetMask(mask_buf, XI_ButtonRelease); XISetMask(mask_buf, XI_RawMotion); XISetMask(mask_buf, XI_KeyPress); XISetMask(mask_buf, XI_KeyRelease); SelectEventsForDevice(DefaultRootWindow(m_display), &mask, pointer_deviceid); SelectEventsForDevice(DefaultRootWindow(m_display), &mask, keyboard_deviceid); // Keyboard Keys for (int i = min_keycode; i <= max_keycode; ++i) { Key* temp_key = new Key(m_display, i, m_state.keyboard); if (temp_key->m_keyname.length()) AddInput(temp_key); else delete temp_key; } // Mouse Buttons for (int i = 0; i < 5; i++) AddInput(new Button(i, m_state.buttons)); // Mouse Cursor, X-/+ and Y-/+ for (int i = 0; i != 4; ++i) AddInput(new Cursor(!!(i & 2), !!(i & 1), (&m_state.cursor.x)[!!(i & 2)])); // Mouse Axis, X-/+ and Y-/+ for (int i = 0; i != 4; ++i) AddInput(new Axis(!!(i & 2), !!(i & 1), (&m_state.axis.x)[!!(i & 2)])); }
int get_master_keyboard_id(Display *display){ int num_devices; XIDeviceInfo *devices = XIQueryDevice(display, XIAllMasterDevices, &num_devices); for (int i = 0; i < num_devices; ++i) { XIDeviceInfo *device = &devices[i]; if (device->use == XIMasterKeyboard){ return device->deviceid; } } return -1; }
static void install_grabs_mouse( void ) { int i; int num_devices; XIDeviceInfo *info; XIEventMask mask; assert( x11display.dpy && x11display.win ); if( mouse_active ) return; XDefineCursor(x11display.dpy, x11display.win, CreateNullCursor(x11display.dpy, x11display.win)); mask.deviceid = XIAllDevices; mask.mask_len = XIMaskLen(XI_LASTEVENT); mask.mask = calloc(mask.mask_len, sizeof(char)); XISetMask(mask.mask, XI_Enter); XISetMask(mask.mask, XI_Leave); XISetMask(mask.mask, XI_ButtonPress); XISetMask(mask.mask, XI_ButtonRelease); info = XIQueryDevice(x11display.dpy, XIAllDevices, &num_devices); for(i = 0; i < num_devices; i++) { int id = info[i].deviceid; if(info[i].use == XISlavePointer) { mask.deviceid = id; XIGrabDevice(x11display.dpy, id, x11display.win, CurrentTime, None, GrabModeSync, GrabModeSync, True, &mask); } else if(info[i].use == XIMasterPointer) { if (x11display.features.wmStateFullscreen) XIWarpPointer(x11display.dpy, id, None, x11display.win, 0, 0, 0, 0, 0, 0); else XIWarpPointer(x11display.dpy, id, None, x11display.win, 0, 0, 0, 0, x11display.win_width/2, x11display.win_height/2); } } XIFreeDeviceInfo(info); mask.deviceid = XIAllDevices; memset(mask.mask, 0, mask.mask_len); XISetMask(mask.mask, XI_RawMotion); XISelectEvents(x11display.dpy, DefaultRootWindow(x11display.dpy), &mask, 1); free(mask.mask); XSync(x11display.dpy, True); mx = my = 0; mouse_active = qtrue; }
static int add_mpx_for_window (char *name) { XIAddMasterInfo add; int ndevices; XIDeviceInfo *devices, *device; int i; int result; /* add the device */ add.type = XIAddMaster; add.name = name; add.send_core = True; add.enable = True; XIChangeHierarchy (gdk_x11_get_default_xdisplay (), (XIAnyHierarchyChangeInfo*) &add, 1); /* now see whether it's in the list */ result = -1; devices = XIQueryDevice(gdk_x11_get_default_xdisplay (), XIAllDevices, &ndevices); for (i = 0; i < ndevices; i++) { device = &devices[i]; if (g_str_has_prefix (device->name, name)) { switch (device->use) { case XIMasterPointer: result = device->deviceid; break; } } } if (result==-1) { g_warning ("The doppelganger pointer '%s' could not be created.", name); } XIFreeDeviceInfo(devices); return result; }
XInput2DeviceData *QXcbConnection::deviceForId(int id) { XInput2DeviceData *dev = m_touchDevices[id]; if (!dev) { int unused = 0; QTouchDevice::Capabilities caps = 0; dev = new XInput2DeviceData; dev->xiDeviceInfo = XIQueryDevice(static_cast<Display *>(m_xlib_display), id, &unused); dev->qtTouchDevice = new QTouchDevice; for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) { XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i]; switch (classinfo->type) { #ifdef XCB_USE_XINPUT22 case XITouchClass: { XITouchClassInfo *tci = reinterpret_cast<XITouchClassInfo *>(classinfo); switch (tci->mode) { case XIModeRelative: dev->qtTouchDevice->setType(QTouchDevice::TouchPad); break; case XIModeAbsolute: dev->qtTouchDevice->setType(QTouchDevice::TouchScreen); break; } } break; #endif case XIValuatorClass: { XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo); if (vci->label == atom(QXcbAtom::AbsMTPositionX)) caps |= QTouchDevice::Position | QTouchDevice::NormalizedPosition; else if (vci->label == atom(QXcbAtom::AbsMTTouchMajor)) caps |= QTouchDevice::Area; else if (vci->label == atom(QXcbAtom::AbsMTPressure) || vci->label == atom(QXcbAtom::AbsPressure)) caps |= QTouchDevice::Pressure; } break; } } dev->qtTouchDevice->setCapabilities(caps); dev->qtTouchDevice->setName(dev->xiDeviceInfo->name); if (caps != 0) QWindowSystemInterface::registerTouchDevice(dev->qtTouchDevice); #ifdef XI2_TOUCH_DEBUG qDebug("registered new device %s with %d classes and %d max touch points", dev->xiDeviceInfo->name, dev->xiDeviceInfo->num_classes, dev->qtTouchDevice->maxTouchPoints()); #endif m_touchDevices[id] = dev; } return dev; }
void XInputMTInputDevice::findMTDevice() { int ndevices; XIDeviceInfo* pDevices; XIDeviceInfo* pDevice; pDevices = XIQueryDevice(s_pDisplay, XIAllDevices, &ndevices); XITouchClassInfo* pTouchClass = 0; for (int i = 0; i < ndevices && !pTouchClass; ++i) { pDevice = &pDevices[i]; // cerr << "Device " << pDevice->name << "(id: " << pDevice->deviceid << ")." // << endl; if (pDevice->use == XISlavePointer || pDevice->use == XIFloatingSlave) { for (int j = 0; j < pDevice->num_classes; ++j) { XIAnyClassInfo * pClass = pDevice->classes[j]; if (pClass->type == XITouchClass) { XITouchClassInfo* pTempTouchClass = (XITouchClassInfo *)pClass; if (pTempTouchClass->mode == XIDirectTouch) { pTouchClass = pTempTouchClass; m_sDeviceName = pDevice->name; m_DeviceID = pDevice->deviceid; if (pDevice->use == XISlavePointer) { m_OldMasterDeviceID = pDevice->attachment; } else { m_OldMasterDeviceID = -1; } break; } } } } } if (pTouchClass) { AVG_TRACE(Logger::category::CONFIG,Logger::severity::INFO, "Using multitouch input device " << m_sDeviceName << ", max touches: " << pTouchClass->num_touches); } else { throw Exception(AVG_ERR_MT_INIT, "XInput multitouch event source: No multitouch device found."); } XIFreeDeviceInfo(pDevices); }
// This function will add zero or more KeyboardMouse objects to devices. void Init(std::vector<Core::Device*>& devices, void* const hwnd) { Display* dpy = XOpenDisplay(nullptr); // xi_opcode is important; it will be used to identify XInput events by // the polling loop in UpdateInput. int xi_opcode, event, error; // verify that the XInput extension is available if (!XQueryExtension(dpy, "XInputExtension", &xi_opcode, &event, &error)) return; // verify that the XInput extension is at at least version 2.0 int major = 2, minor = 0; if (XIQueryVersion(dpy, &major, &minor) != Success) return; // register all master devices with Dolphin XIDeviceInfo* all_masters; XIDeviceInfo* current_master; int num_masters; all_masters = XIQueryDevice(dpy, XIAllMasterDevices, &num_masters); for (int i = 0; i < num_masters; i++) { current_master = &all_masters[i]; if (current_master->use == XIMasterPointer) // Since current_master is a master pointer, its attachment must // be a master keyboard. devices.push_back(new KeyboardMouse((Window)hwnd, xi_opcode, current_master->deviceid, current_master->attachment)); } XCloseDisplay(dpy); XIFreeDeviceInfo(all_masters); }
static void install_grabs_keyboard( void ) { int i; int num_devices; XIDeviceInfo *info; XIEventMask mask; assert( x11display.dpy && x11display.win ); if( input_active ) return; XDefineCursor(x11display.dpy, x11display.win, CreateNullCursor(x11display.dpy, x11display.win)); mask.deviceid = XIAllMasterDevices; mask.mask_len = XIMaskLen(XI_LASTEVENT); mask.mask = calloc(mask.mask_len, sizeof(char)); XISetMask(mask.mask, XI_KeyPress); XISetMask(mask.mask, XI_KeyRelease); XISetMask(mask.mask, XI_ButtonPress); XISetMask(mask.mask, XI_ButtonRelease); XISelectEvents(x11display.dpy, x11display.win, &mask, 1); info = XIQueryDevice(x11display.dpy, XIAllDevices, &num_devices); for(i = 0; i < num_devices; i++) { int id = info[i].deviceid; if(info[i].use == XIMasterKeyboard) { XIGrabDevice(x11display.dpy, id, x11display.win, CurrentTime, None, GrabModeAsync, GrabModeAsync, False, &mask); } } XIFreeDeviceInfo(info); free(mask.mask); XSync(x11display.dpy, True); input_active = qtrue; }
static void uninstall_grabs_keyboard( void ) { int i; int num_devices; XIDeviceInfo *info; assert( x11display.dpy && x11display.win ); if( !input_active ) return; info = XIQueryDevice(x11display.dpy, XIAllDevices, &num_devices); for(i = 0; i < num_devices; i++) { if(info[i].use == XIMasterKeyboard) { XIUngrabDevice(x11display.dpy, info[i].deviceid, CurrentTime); } } XIFreeDeviceInfo(info); input_active = qfalse; }
/** * generate a new mouse pointer * @param name given a mouse pointer name * @return On success, generated mouse pointer's device id * On failure, return -1 */ int GeneratePointer(char *name){ XIAddMasterInfo c; XIDeviceInfo *info; int ndevices, deviceid, i; const int MaxNameLen = 1024; char devicename[MaxNameLen]; deviceid = -1; c.type = XIAddMaster; c.name = name; c.send_core = 1; c.enable = 1; Status s = XIChangeHierarchy(dpy, (XIAnyHierarchyChangeInfo*)&c, 1); if (!s) { // get device id info = XIQueryDevice(dpy, XIAllDevices, &ndevices); strncpy(devicename, name, MaxNameLen); strncat(devicename, " pointer", MaxNameLen); for(i = 0; i < ndevices; i++) { // printf("device: %s\t id: %d\n", info[i].name, info[i].deviceid); if (!strncmp(info[i].name, devicename, MaxNameLen)) { deviceid = info[i].deviceid; break; } } } return deviceid; }
void _ecore_x_input_init(void) { #ifdef ECORE_XI2 int event, error; int major = 2, minor = 0; if (!XQueryExtension(_ecore_x_disp, "XInputExtension", &_ecore_x_xi2_opcode, &event, &error)) { _ecore_x_xi2_opcode = -1; return; } if (XIQueryVersion(_ecore_x_disp, &major, &minor) == BadRequest) { _ecore_x_xi2_opcode = -1; return; } _ecore_x_xi2_devs = XIQueryDevice(_ecore_x_disp, XIAllDevices, &_ecore_x_xi2_num); #endif /* ifdef ECORE_XI2 */ } /* _ecore_x_input_init */
void QXcbConnection::xi2SetupDevices() { #ifndef QT_NO_TABLETEVENT m_tabletData.clear(); #endif m_scrollingDevices.clear(); if (!m_xi2Enabled) return; Display *xDisplay = static_cast<Display *>(m_xlib_display); int deviceCount = 0; XIDeviceInfo *devices = XIQueryDevice(xDisplay, XIAllDevices, &deviceCount); for (int i = 0; i < deviceCount; ++i) { // Only non-master pointing devices are relevant here. if (devices[i].use != XISlavePointer) continue; qCDebug(lcQpaXInputDevices) << "input device "<< devices[i].name; #ifndef QT_NO_TABLETEVENT TabletData tabletData; #endif ScrollingDevice scrollingDevice; for (int c = 0; c < devices[i].num_classes; ++c) { switch (devices[i].classes[c]->type) { case XIValuatorClass: { XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(devices[i].classes[c]); const int valuatorAtom = qatom(vci->label); qCDebug(lcQpaXInputDevices) << " has valuator" << atomName(vci->label) << "recognized?" << (valuatorAtom < QXcbAtom::NAtoms); #ifndef QT_NO_TABLETEVENT if (valuatorAtom < QXcbAtom::NAtoms) { TabletData::ValuatorClassInfo info; info.minVal = vci->min; info.maxVal = vci->max; info.number = vci->number; tabletData.valuatorInfo[valuatorAtom] = info; } #endif // QT_NO_TABLETEVENT if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel) scrollingDevice.lastScrollPosition.setX(vci->value); else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel) scrollingDevice.lastScrollPosition.setY(vci->value); break; } #ifdef XCB_USE_XINPUT21 case XIScrollClass: { XIScrollClassInfo *sci = reinterpret_cast<XIScrollClassInfo *>(devices[i].classes[c]); if (sci->scroll_type == XIScrollTypeVertical) { scrollingDevice.orientations |= Qt::Vertical; scrollingDevice.verticalIndex = sci->number; scrollingDevice.verticalIncrement = sci->increment; } else if (sci->scroll_type == XIScrollTypeHorizontal) { scrollingDevice.orientations |= Qt::Horizontal; scrollingDevice.horizontalIndex = sci->number; scrollingDevice.horizontalIncrement = sci->increment; } break; } case XIButtonClass: { XIButtonClassInfo *bci = reinterpret_cast<XIButtonClassInfo *>(devices[i].classes[c]); if (bci->num_buttons >= 5) { Atom label4 = bci->labels[3]; Atom label5 = bci->labels[4]; // Some drivers have no labels on the wheel buttons, some have no label on just one and some have no label on // button 4 and the wrong one on button 5. So we just check that they are not labelled with unrelated buttons. if ((!label4 || qatom(label4) == QXcbAtom::ButtonWheelUp || qatom(label4) == QXcbAtom::ButtonWheelDown) && (!label5 || qatom(label5) == QXcbAtom::ButtonWheelUp || qatom(label5) == QXcbAtom::ButtonWheelDown)) scrollingDevice.legacyOrientations |= Qt::Vertical; } if (bci->num_buttons >= 7) { Atom label6 = bci->labels[5]; Atom label7 = bci->labels[6]; if ((!label6 || qatom(label6) == QXcbAtom::ButtonHorizWheelLeft) && (!label7 || qatom(label7) == QXcbAtom::ButtonHorizWheelRight)) scrollingDevice.legacyOrientations |= Qt::Horizontal; } qCDebug(lcQpaXInputDevices, " has %d buttons", bci->num_buttons); break; } #endif case XIKeyClass: qCDebug(lcQpaXInputDevices) << " it's a keyboard"; break; #ifdef XCB_USE_XINPUT22 case XITouchClass: // will be handled in deviceForId() break; #endif default: qCDebug(lcQpaXInputDevices) << " has class" << devices[i].classes[c]->type; break; } } bool isTablet = false; #ifndef QT_NO_TABLETEVENT // If we have found the valuators which we expect a tablet to have, it might be a tablet. if (tabletData.valuatorInfo.contains(QXcbAtom::AbsX) && tabletData.valuatorInfo.contains(QXcbAtom::AbsY) && tabletData.valuatorInfo.contains(QXcbAtom::AbsPressure)) isTablet = true; // But we need to be careful not to take the touch and tablet-button devices as tablets. QByteArray name = QByteArray(devices[i].name).toLower(); QString dbgType = QLatin1String("UNKNOWN"); if (name.contains("eraser")) { isTablet = true; tabletData.pointerType = QTabletEvent::Eraser; dbgType = QLatin1String("eraser"); } else if (name.contains("cursor")) { isTablet = true; tabletData.pointerType = QTabletEvent::Cursor; dbgType = QLatin1String("cursor"); } else if ((name.contains("pen") || name.contains("stylus")) && isTablet) { tabletData.pointerType = QTabletEvent::Pen; dbgType = QLatin1String("pen"); } else if (name.contains("wacom") && isTablet && !name.contains("touch")) { // combined device (evdev) rather than separate pen/eraser (wacom driver) tabletData.pointerType = QTabletEvent::Pen; dbgType = QLatin1String("pen"); } else if (name.contains("aiptek") /* && device == QXcbAtom::KEYBOARD */) { // some "Genius" tablets isTablet = true; tabletData.pointerType = QTabletEvent::Pen; dbgType = QLatin1String("pen"); } else { isTablet = false; } if (isTablet) { tabletData.deviceId = devices[i].deviceid; m_tabletData.append(tabletData); qCDebug(lcQpaXInputDevices) << " it's a tablet with pointer type" << dbgType; } #endif // QT_NO_TABLETEVENT #ifdef XCB_USE_XINPUT21 if (scrollingDevice.orientations || scrollingDevice.legacyOrientations) { scrollingDevice.deviceId = devices[i].deviceid; // Only use legacy wheel button events when we don't have real scroll valuators. scrollingDevice.legacyOrientations &= ~scrollingDevice.orientations; m_scrollingDevices.insert(scrollingDevice.deviceId, scrollingDevice); qCDebug(lcQpaXInputDevices) << " it's a scrolling device"; } #endif if (!isTablet) { // touchDeviceForId populates XInput2DeviceData the first time it is called // with a new deviceId. On subsequent calls it will return the cached object. XInput2TouchDeviceData *dev = touchDeviceForId(devices[i].deviceid); if (dev && lcQpaXInputDevices().isDebugEnabled()) { if (dev->qtTouchDevice->type() == QTouchDevice::TouchScreen) qCDebug(lcQpaXInputDevices, " it's a touchscreen with type %d capabilities 0x%X max touch points %d", dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(), dev->qtTouchDevice->maximumTouchPoints()); else if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad) qCDebug(lcQpaXInputDevices, " it's a touchpad with type %d capabilities 0x%X max touch points %d size %f x %f", dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(), dev->qtTouchDevice->maximumTouchPoints(), dev->size.width(), dev->size.height()); } } } XIFreeDeviceInfo(devices); }
bool xorg::testing::XServer::WaitForDevice(::Display *display, const std::string &name, time_t timeout) { int opcode; int event_start; int error_start; bool device_found = false; if (!XQueryExtension(display, "XInputExtension", &opcode, &event_start, &error_start)) throw std::runtime_error("Failed to query XInput extension"); XIEventMask *masks; int nmasks; bool mask_set, mask_created; masks = set_hierarchy_mask(display, &nmasks, &mask_set, &mask_created); XIDeviceInfo *info; int ndevices; info = XIQueryDevice(display, XIAllDevices, &ndevices); for (int i = 0; !device_found && i < ndevices; i++) { device_found = (name.compare(info[i].name) == 0); } XIFreeDeviceInfo(info); while (!device_found && WaitForEventOfType(display, GenericEvent, opcode, XI_HierarchyChanged, timeout)) { XEvent event; if (XNextEvent(display, &event) != Success) throw std::runtime_error("Failed to get X event"); XGenericEventCookie *xcookie = reinterpret_cast<XGenericEventCookie*>(&event.xcookie); if (!XGetEventData(display, xcookie)) throw std::runtime_error("Failed to get X event data"); XIHierarchyEvent *hierarchy_event = reinterpret_cast<XIHierarchyEvent*>(xcookie->data); if (!(hierarchy_event->flags & XIDeviceEnabled)) { XFreeEventData(display, xcookie); continue; } device_found = false; for (int i = 0; i < hierarchy_event->num_info; i++) { if (!(hierarchy_event->info[i].flags & XIDeviceEnabled)) continue; int num_devices; XIDeviceInfo *device_info = XIQueryDevice(display, hierarchy_event->info[i].deviceid, &num_devices); if (num_devices != 1 || !device_info) throw std::runtime_error("Failed to query device"); if (name.compare(device_info[0].name) == 0) { device_found = true; break; } } XFreeEventData(display, xcookie); if (device_found) break; } unset_hierarchy_mask(display, masks, nmasks, mask_set, mask_created); return device_found; }
void setup_input_devices (GromitData *data) { /* ungrab all */ release_grab (data, NULL); /* and clear our own device data list */ GHashTableIter it; gpointer value; g_hash_table_iter_init (&it, data->devdatatable); while (g_hash_table_iter_next (&it, NULL, &value)) g_free(value); g_hash_table_remove_all(data->devdatatable); /* get devices */ GdkDeviceManager *device_manager = gdk_display_get_device_manager(data->display); GList *devices, *d; int i = 0; devices = g_list_concat(gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER), gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_SLAVE)); for(d = devices; d; d = d->next) { GdkDevice *device = (GdkDevice *) d->data; /* only enable devices with 2 ore more axes */ if (gdk_device_get_source(device) != GDK_SOURCE_KEYBOARD && gdk_device_get_n_axes(device) >= 2) { gdk_device_set_mode (device, GDK_MODE_SCREEN); GromitDeviceData *devdata; devdata = g_malloc0(sizeof (GromitDeviceData)); devdata->device = device; devdata->index = i; /* get attached keyboard and grab the hotkey */ if (!data->hot_keycode) { g_printerr("ERROR: Grabbing hotkey from attached keyboard " "of '%s' failed, no hotkey defined.\n", gdk_device_get_name(device)); g_free(devdata); continue; } /* if this is a slave device, we need the master */ GdkDevice *kdevice=device; if(gdk_device_get_device_type (device) == GDK_DEVICE_TYPE_SLAVE) kdevice=gdk_device_get_associated_device (device); gint dev_id = -1; g_object_get(kdevice, "device-id", &dev_id, NULL); gint kbd_dev_id = -1; XIDeviceInfo* devinfo; int devicecount = 0; devinfo = XIQueryDevice(GDK_DISPLAY_XDISPLAY(data->display), dev_id, &devicecount); if(devicecount) kbd_dev_id = devinfo->attachment; XIFreeDeviceInfo(devinfo); if(kbd_dev_id != -1) { if(data->debug) g_printerr("DEBUG: Grabbing hotkey from keyboard '%d' .\n", kbd_dev_id); XIEventMask mask; unsigned char bits[4] = {0,0,0,0}; mask.mask = bits; mask.mask_len = sizeof(bits); XISetMask(bits, XI_KeyPress); XISetMask(bits, XI_KeyRelease); XIGrabModifiers modifiers[] = {{XIAnyModifier, 0}}; int nmods = 1; gdk_error_trap_push (); XIGrabKeycode( GDK_DISPLAY_XDISPLAY(data->display), kbd_dev_id, data->hot_keycode, GDK_WINDOW_XID(data->root), GrabModeAsync, GrabModeAsync, True, &mask, nmods, modifiers); XSync(GDK_DISPLAY_XDISPLAY(data->display), False); if(gdk_error_trap_pop()) { g_printerr("ERROR: Grabbing hotkey from keyboard device %d failed.\n", kbd_dev_id); g_free(devdata); continue; } } g_hash_table_insert(data->devdatatable, device, devdata); g_printerr ("Enabled Device %d: \"%s\", (Type: %d)\n", i++, gdk_device_get_name(device), gdk_device_get_source(device)); } } g_printerr ("Now %d enabled devices.\n", g_hash_table_size(data->devdatatable)); }
// Constructor CalibratorEvdev::CalibratorEvdev(const char* const device_name0, const XYinfo& axys0, XID device_id, const int thr_misclick, const int thr_doubleclick, const OutputType output_type, const char* geometry, const bool use_valuator, const bool use_timeout, const char* output_filename) : Calibrator(device_name0, axys0, thr_misclick, thr_doubleclick, output_type, geometry, use_timeout, output_filename) { // init display = XOpenDisplay(NULL); if (display == NULL) { throw WrongCalibratorException("Evdev: Unable to connect to X server"); } // normaly, we already have the device id if (device_id == (XID)-1) { devInfo = xinput_find_device_info(display, device_name, False); if (!devInfo) { XCloseDisplay(display); throw WrongCalibratorException("Evdev: Unable to find device"); } device_id = devInfo->id; } dev = XOpenDevice(display, device_id); if (!dev) { XCloseDisplay(display); throw WrongCalibratorException("Evdev: Unable to open device"); } #ifndef HAVE_XI_PROP throw WrongCalibratorException("Evdev: you need at least libXi 1.2 and inputproto 1.5 for dynamic recalibration of evdev."); #else // XGetDeviceProperty vars Atom property; Atom act_type; int act_format; unsigned long nitems, bytes_after; unsigned char *data, *ptr; // get "Evdev Axis Calibration" property property = xinput_parse_atom(display, "Evdev Axis Calibration"); if (XGetDeviceProperty(display, dev, property, 0, 1000, False, AnyPropertyType, &act_type, &act_format, &nitems, &bytes_after, &data) != Success) { XCloseDevice(display, dev); XCloseDisplay(display); throw WrongCalibratorException("Evdev: \"Evdev Axis Calibration\" property missing, not a (valid) evdev device"); } else { if (act_format != 32 || act_type != XA_INTEGER) { XCloseDevice(display, dev); XCloseDisplay(display); throw WrongCalibratorException("Evdev: invalid \"Evdev Axis Calibration\" property format"); } else if (use_valuator) { int ndevices = 0, i, j; XIDeviceInfo *info = XIQueryDevice(display, device_id, &ndevices); if (ndevices != 1) { XCloseDevice(display, dev); XCloseDisplay(display); throw WrongCalibratorException("Evdev: unknown Xinput device ID???"); } for (i = 0; i < ndevices; i++) { XIDeviceInfo *dev = &info[i]; for (j = 0; j < dev->num_classes; j++) { switch(dev->classes[i]->type) { case XIValuatorClass: { XIValuatorClassInfo *v = (XIValuatorClassInfo*)dev->classes[i]; /* Valuator 0 = X, Valuator 1 = Y, others are ignored */ switch (v->number) { case 0: old_axys.x.min = v->min; old_axys.x.max = v->max; old_axys.x.invert = false; break; case 1: old_axys.y.min = v->min; old_axys.y.max = v->max; old_axys.y.invert = false; break; default: break; } break; } default: break; } } } XIFreeDeviceInfo(info); if (verbose) printf("DEBUG: Evdev Axis Calibration set to axis valuators\n"); (void) set_calibration(old_axys); } else if (nitems == 0) { if (verbose) printf("DEBUG: Evdev Axis Calibration not set, setting to axis valuators to be sure.\n"); // No axis calibration set, set it to the default one // QUIRK: when my machine resumes from a sleep, // the calibration property is no longer exported through xinput, but still active // not setting the values here would result in a wrong first calibration (void) set_calibration(old_axys); } else if (nitems > 0) { ptr = data; old_axys.x.min = *((long*)ptr); ptr += sizeof(long); old_axys.x.max = *((long*)ptr); ptr += sizeof(long); old_axys.y.min = *((long*)ptr); ptr += sizeof(long); old_axys.y.max = *((long*)ptr); ptr += sizeof(long); } XFree(data); } // get "Evdev Axes Swap" property property = xinput_parse_atom(display, "Evdev Axes Swap"); if (XGetDeviceProperty(display, dev, property, 0, 1000, False, AnyPropertyType, &act_type, &act_format, &nitems, &bytes_after, &data) == Success) { if (act_format == 8 && act_type == XA_INTEGER && nitems == 1) { old_axys.swap_xy = *((char*)data); if (verbose) printf("DEBUG: Read axes swap value of %i.\n", old_axys.swap_xy); } } // get "Evdev Axes Inversion" property property = xinput_parse_atom(display, "Evdev Axis Inversion"); if (XGetDeviceProperty(display, dev, property, 0, 1000, False, AnyPropertyType, &act_type, &act_format, &nitems, &bytes_after, &data) == Success) { if (act_format == 8 && act_type == XA_INTEGER && nitems == 2) { old_axys.x.invert = *((char*)data++); old_axys.y.invert = *((char*)data); if (verbose) printf("DEBUG: Read InvertX=%i, InvertY=%i.\n", old_axys.x.invert, old_axys.y.invert); } } printf("Calibrating EVDEV driver for \"%s\" id=%i\n", device_name, (int)device_id); printf("\tcurrent calibration values (from XInput): min_x=%d, max_x=%d and min_y=%d, max_y=%d\n", old_axys.x.min, old_axys.x.max, old_axys.y.min, old_axys.y.max); #endif // HAVE_XI_PROP }
int xinput_init() { int ret = 0; int event, error; int m_num = 0; int k_num = 0; memset(devices, 0x00, sizeof(devices)); nb_devices = 0; unsigned int i; for(i=0; i<sizeof(device_index)/sizeof(*device_index); ++i) { device_index[i] = -1; } dpy = XOpenDisplay(NULL); if (!dpy) { fprintf(stderr, "Failed to open display.\n"); return -1; } if (!XQueryExtension(dpy, "XInputExtension", &xi_opcode, &event, &error)) { fprintf(stderr, "X Input extension not available.\n"); return -1; } win = create_win(dpy); int nxdevices; XIDeviceInfo *xdevices, *xdevice; xdevices = XIQueryDevice(dpy, XIAllDevices, &nxdevices); for (i = 0; nxdevices > 0 && i < (unsigned int) nxdevices; i++) { xdevice = &xdevices[i]; if(xdevice->deviceid >= GE_MAX_DEVICES) { continue; } device_index[xdevice->deviceid] = nb_devices; switch(xdevice->use) { case XISlaveKeyboard: devices[nb_devices].type = DEVTYPE_KEYBOARD; devices[nb_devices].id = k_num; ++k_num; break; case XISlavePointer: devices[nb_devices].type = DEVTYPE_MOUSE; devices[nb_devices].id = m_num; ++m_num; break; } if(devices[nb_devices].type) { devices[nb_devices].name = strdup(xdevice->name); ++nb_devices; } } XIFreeDeviceInfo(xdevices); ev_register_source(ConnectionNumber(dpy), i, &xinput_process_events, NULL, &xinput_close); XEvent xevent; /* get info about current pointer position */ XQueryPointer(dpy, DefaultRootWindow(dpy), &xevent.xbutton.root, &xevent.xbutton.window, &xevent.xbutton.x_root, &xevent.xbutton.y_root, &xevent.xbutton.x, &xevent.xbutton.y, &xevent.xbutton.state); mouse_coordinates.x = xevent.xbutton.x; mouse_coordinates.y = xevent.xbutton.y; return ret; }
/** * Build data storage by querying the X server for all input devices. * Can be called multiple times, in which case it'll clean out and re-fill * update the tree store. */ static GtkTreeStore* query_devices(GDeviceSetup* gds) { GtkTreeStore *treestore; GtkTreeModel *model; GtkTreeIter iter, child; XIDeviceInfo *devices, *dev; int ndevices; int i, j; int icontype; GdkPixbuf *icon; int valid, child_valid; int id, masterid; if (!gds->treeview) { treestore = gtk_tree_store_new(NUM_COLS, G_TYPE_UINT, /* deviceid*/ G_TYPE_STRING, /* name */ G_TYPE_UINT, GDK_TYPE_PIXBUF, G_TYPE_UINT ); model = GTK_TREE_MODEL(treestore); } else { model = gtk_tree_view_get_model(gds->treeview); treestore = GTK_TREE_STORE(model); } gds->generation++; devices = XIQueryDevice(gds->dpy, XIAllDevices, &ndevices); /* First, run through all master device and append them to the tree store */ for (i = 0; i < ndevices; i++) { dev = &devices[i]; if (dev->use != XIMasterPointer && dev->use != XIMasterKeyboard) continue; valid = gtk_tree_model_get_iter_first(model, &iter); g_debug("MD %d: %s", dev->deviceid, dev->name); while(valid) { gtk_tree_model_get(model, &iter, COL_ID, &id, -1); if (id == dev->deviceid) { gtk_tree_store_set(treestore, &iter, COL_GENERATION, gds->generation, -1); valid = 0xFF; break; } valid = gtk_tree_model_iter_next(model, &iter); } if (valid != 0xFF) /* new MD */ { icontype = (dev->use == XIMasterPointer) ? ICON_MOUSE : ICON_KEYBOARD; icon = load_icon(icontype); gtk_tree_store_append(treestore, &iter, NULL); gtk_tree_store_set(treestore, &iter, COL_ID, dev->deviceid, COL_NAME, dev->name, COL_USE, dev->use, COL_ICON, icon, COL_GENERATION, gds->generation, -1); g_object_unref(icon); } } /* search for Floating fake master device */ valid = gtk_tree_model_get_iter_first(model, &iter); while(valid) { gtk_tree_model_get(model, &iter, COL_ID, &id, -1); if (id == ID_FLOATING) break; valid = gtk_tree_model_iter_next(model, &iter); } if (!valid) { /* Attach a fake master device for "Floating" */ icon = load_icon(ICON_FLOATING); gtk_tree_store_append(treestore, &iter, NULL); gtk_tree_store_set(treestore, &iter, COL_ID, ID_FLOATING, COL_NAME, "Floating", COL_USE, ID_FLOATING, COL_ICON, icon, COL_GENERATION, gds->generation, -1); g_object_unref(icon); } else { GtkTreeIter prev; GtkTreeIter pos = iter; /* current position of Floating */ /* always move Floating fake device to end of list */ while(valid) { prev = iter; valid = gtk_tree_model_iter_next(model, &iter); } gtk_tree_store_move_after(treestore, &pos, &prev); /* update generation too */ gtk_tree_store_set(treestore, &pos, COL_GENERATION, gds->generation, -1); } /* now that we added all MDs, run through again and add SDs to the * respective MD */ for (i = 0; i < ndevices; i++) { dev = &devices[i]; if (dev->use == XIMasterPointer || dev->use == XIMasterKeyboard) continue; g_debug("SD %d: %s", dev->deviceid, dev->name); valid = gtk_tree_model_get_iter_first(model, &iter); while(valid) { gtk_tree_model_get(model, &iter, COL_ID, &masterid, -1); if(dev->attachment == masterid || (dev->use == XIFloatingSlave && masterid == ID_FLOATING)) { /* found master, check if we're already attached to it in * the tree model */ child_valid = gtk_tree_model_iter_children(model, &child, &iter); while (child_valid) { gtk_tree_model_get(model, &child, COL_ID, &id); if (id == dev->deviceid) { gtk_tree_store_set(treestore, &child, COL_GENERATION, gds->generation, -1); child_valid = 0xFF; break; } child_valid = gtk_tree_model_iter_next(model, &child); } /* new slave device, attach */ if (child_valid != 0xFF) { gtk_tree_store_append(treestore, &child, &iter); gtk_tree_store_set(treestore, &child, COL_ID, dev->deviceid, COL_NAME, dev->name, COL_USE, dev->use, COL_GENERATION, gds->generation, -1); } break; } valid = gtk_tree_model_iter_next(model, &iter); } } XIFreeDeviceInfo(devices); /* clean tree store of anything that doesn't have the current server generation */ valid = gtk_tree_model_get_iter_first(model, &iter); while(valid) { int gen; child_valid = gtk_tree_model_iter_children(model, &child, &iter); while(child_valid) { gtk_tree_model_get(model, &child, COL_GENERATION, &gen, -1); if (gen < gds->generation) child_valid = gtk_tree_store_remove(treestore, &child); else child_valid = gtk_tree_model_iter_next(model, &child); } gtk_tree_model_get(model, &iter, COL_GENERATION, &gen, -1); if (gen < gds->generation) valid = gtk_tree_store_remove(treestore, &iter); else valid = gtk_tree_model_iter_next(model, &iter); } return treestore; }
void X11Extras::x11ResetMouseAccelerationChange() { //QTextStream out(stdout); int xi_opcode, event, error; xi_opcode = event = error = 0; Display *display = this->display(); bool result = XQueryExtension(display, "XInputExtension", &xi_opcode, &event, &error); if (!result) { Logger::LogInfo(tr("xinput extension was not found. No mouse acceleration changes will occur.")); //out << tr("xinput extension was not found. No mouse acceleration changes will occur.") << endl; } else { int ximajor = 2, ximinor = 0; if (XIQueryVersion(display, &ximajor, &ximinor) != Success) { Logger::LogInfo(tr("xinput version must be at least 2.0. No mouse acceleration changes will occur.")); //out << tr("xinput version must be at least 2.0. No mouse acceleration changes will occur.") << endl; } } if (result) { XIDeviceInfo *all_devices = 0; XIDeviceInfo *current_devices = 0; XIDeviceInfo *mouse_device = 0; int num_devices = 0; all_devices = XIQueryDevice(display, XIAllDevices, &num_devices); for (int i=0; i < num_devices; i++) { current_devices = &all_devices[i]; if (current_devices->use == XISlavePointer && QString::fromUtf8(current_devices->name) == mouseDeviceName) { Logger::LogInfo(tr("Virtual pointer found with id=%1.").arg(current_devices->deviceid)); //out << tr("Virtual pointer found with id=%1.").arg(current_devices->deviceid) // << endl; mouse_device = current_devices; } } if (mouse_device) { XDevice *device = XOpenDevice(display, mouse_device->deviceid); int num_feedbacks = 0; int feedback_id = -1; XFeedbackState *feedbacks = XGetFeedbackControl(display, device, &num_feedbacks); XFeedbackState *temp = feedbacks; for (int i=0; (i < num_feedbacks) && (feedback_id == -1); i++) { if (temp->c_class == PtrFeedbackClass) { feedback_id = temp->id; } if (i+1 < num_feedbacks) { temp = (XFeedbackState*) ((char*) temp + temp->length); } } XFree(feedbacks); feedbacks = temp = 0; if (feedback_id <= -1) { Logger::LogInfo(tr("PtrFeedbackClass was not found for virtual pointer." "No change to mouse acceleration will occur for device with id=%1").arg(device->device_id)); //out << tr("PtrFeedbackClass was not found for virtual pointer." // "No change to mouse acceleration will occur for device with id=%1").arg(device->device_id) // << endl; result = false; } else { Logger::LogInfo(tr("Changing mouse acceleration for device with id=%1").arg(device->device_id)); //out << tr("Changing mouse acceleration for device with id=%1").arg(device->device_id) // << endl; XPtrFeedbackControl feedback; feedback.c_class = PtrFeedbackClass; feedback.length = sizeof(XPtrFeedbackControl); feedback.id = feedback_id; feedback.threshold = 0; feedback.accelNum = 1; feedback.accelDenom = 1; XChangeFeedbackControl(display, device, DvAccelNum|DvAccelDenom|DvThreshold, (XFeedbackControl*) &feedback); XSync(display, false); } XCloseDevice(display, device); } if (all_devices) { XIFreeDeviceInfo(all_devices); } } }
/* Reads the calibration data from evdev, should be self-explanatory. */ void readCalibrationData(int exitOnFail) { if(debugMode) { printf("Start calibration\n"); } Atom retType; int retFormat; unsigned long retItems, retBytesAfter; unsigned int* data; if(XIGetProperty(display, deviceID, XInternAtom(display, "Evdev Axis Calibration", 0), 0, 4 * 32, False, XA_INTEGER, &retType, &retFormat, &retItems, &retBytesAfter, (unsigned char**) &data) != Success) { data = NULL; } if (data == NULL || retItems != 4 || data[0] == data[1] || data[2] == data[3]) { /* evdev might not be ready yet after resume. Let's wait a second and try again. */ sleep(1); if(XIGetProperty(display, deviceID, XInternAtom(display, "Evdev Axis Calibration", 0), 0, 4 * 32, False, XA_INTEGER, &retType, &retFormat, &retItems, &retBytesAfter, (unsigned char**) &data) != Success) { return; } if (retItems != 4 || data[0] == data[1] || data[2] == data[3]) { if(debugMode) { printf("No calibration data found, use default values.\n"); } /* Get minimum/maximum of axes */ int nDev; XIDeviceInfo * deviceInfo = XIQueryDevice(display, deviceID, &nDev); int c; for(c = 0; c < deviceInfo->num_classes; c++) { if(deviceInfo->classes[c]->type == XIValuatorClass) { XIValuatorClassInfo* valuatorInfo = (XIValuatorClassInfo *) deviceInfo->classes[c]; if(valuatorInfo->mode == XIModeAbsolute) { if(valuatorInfo->label == XInternAtom(display, "Abs X", 0) || valuatorInfo->label == XInternAtom(display, "Abs MT Position X", 0)) { calibMinX = valuatorInfo->min; calibMaxX = valuatorInfo->max; } else if(valuatorInfo->label == XInternAtom(display, "Abs Y", 0) || valuatorInfo->label == XInternAtom(display, "Abs MT Position Y", 0)) { calibMinY = valuatorInfo->min; calibMaxY = valuatorInfo->max; } } } } XIFreeDeviceInfo(deviceInfo); } else { calibMinX = data[0]; calibMaxX = data[1]; calibMinY = data[2]; calibMaxY = data[3]; } } else { calibMinX = data[0]; calibMaxX = data[1]; calibMinY = data[2]; calibMaxY = data[3]; } if(data != NULL) { XFree(data); } float * data4 = NULL; if(XIGetProperty(display, deviceID, XInternAtom(display, "Coordinate Transformation Matrix", 0), 0, 9 * 32, False, XInternAtom(display, "FLOAT", 0), &retType, &retFormat, &retItems, &retBytesAfter, (unsigned char **) &data4) != Success) { data4 = NULL; } calibMatrixUse = 0; if(data4 != NULL && retItems == 9) { int i; for(i = 0; i < 6; i++) { /* We only take the first two rows of the matrix, the rest is unimportant anyway */ calibMatrix[i] = data4[i]; } calibMatrixUse = 1; } if(data4 != NULL) { XFree(data4); } unsigned char* data2; if(XIGetProperty(display, deviceID, XInternAtom(display, "Evdev Axis Inversion", 0), 0, 2 * 8, False, XA_INTEGER, &retType, &retFormat, &retItems, &retBytesAfter, (unsigned char**) &data2) != Success) { return; } if (retItems != 2) { if (exitOnFail) { printf("No valid axis inversion data found.\n"); exit(1); } else { return; } } calibSwapX = data2[0]; calibSwapY = data2[1]; XFree(data2); if(XIGetProperty(display, deviceID, XInternAtom(display, "Evdev Axes Swap", 0), 0, 8, False, XA_INTEGER, &retType, &retFormat, &retItems, &retBytesAfter, (unsigned char**) &data2) != Success) { return; } if (retItems != 1) { if (exitOnFail) { printf("No valid axes swap data found.\n"); exit(1); } else { return; } } calibSwapAxes = data2[0]; XFree(data2); if(debugMode) { printf("Calibration: MinX: %i; MaxX: %i; MinY: %i; MaxY: %i\n", calibMinX, calibMaxX, calibMinY, calibMaxY); } }
PsychError SCREENGetMouseHelper(void) { const char *valuatorInfo[]={"label", "min", "max", "resolution", "mode", "sourceID"}; int numValuatorStructFieldNames = 6; int numIValuators = 0; PsychGenericScriptType *valuatorStruct = NULL; #if PSYCH_SYSTEM == PSYCH_OSX Point mouseXY; UInt32 buttonState; double *buttonArray; int numButtons, i; psych_bool doButtonArray; PsychWindowRecordType *windowRecord; //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //cap the numbers of inputs and outputs PsychErrorExit(PsychCapNumInputArgs(3)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(6)); //The maximum number of outputs //Buttons. // The only way I know to detect the number number of mouse buttons is directly via HID. The device reports //that information but OS X seems to ignore it above the level of the HID driver, that is, no OS X API above the HID driver //exposes it. So GetMouse.m function calls PsychHID detect the number of buttons and then passes that value to GetMouseHelper //which returns that number of button values in a vector. PsychCopyInIntegerArg(1, kPsychArgRequired, &numButtons); if(numButtons > 32) PsychErrorExitMsg(PsychErorr_argumentValueOutOfRange, "numButtons must not exceed 32"); // Special codes -10 to -15? --> Console keyboard queries: if(numButtons <= -10 && numButtons >= -15) { ConsoleInputHelper((int) numButtons); return(PsychError_none); } if(numButtons < 1) PsychErrorExitMsg(PsychErorr_argumentValueOutOfRange, "numButtons must exceed 1"); doButtonArray=PsychAllocOutDoubleMatArg(3, kPsychArgOptional, (int)1, (int)numButtons, (int)1, &buttonArray); if(doButtonArray){ buttonState=GetCurrentButtonState(); for(i=0;i<numButtons;i++) buttonArray[i]=(double)(buttonState & (1<<i)); } // Get cursor position: #ifndef __LP64__ // 32-Bit Carbon version: GetGlobalMouse(&mouseXY); PsychCopyOutDoubleArg(1, kPsychArgOptional, (double)mouseXY.h); PsychCopyOutDoubleArg(2, kPsychArgOptional, (double)mouseXY.v); #else // 64-Bit HIToolbox version (OSX 10.5 and later): HIPoint outPoint; HIGetMousePosition(kHICoordSpaceScreenPixel, NULL, &outPoint); PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) outPoint.x); PsychCopyOutDoubleArg(2, kPsychArgOptional, (double) outPoint.y); #endif // Return optional keyboard input focus status: if (numButtons > 0) { // Window provided? // We only have the function GetUserFocusWindow on 32-Bit Carbon. // We have a drop-in replacement in OSX/PsychCocoaGlue.c for 64-Bit Cocoa. if (PsychIsWindowIndexArg(2)) { // Yes: Check if it has focus. PsychAllocInWindowRecordArg(2, TRUE, &windowRecord); if (!PsychIsOnscreenWindow(windowRecord)) { PsychErrorExitMsg(PsychError_user, "Provided window handle isn't an onscreen window, as required."); } PsychCopyOutDoubleArg(4, kPsychArgOptional, (double) (GetUserFocusWindow() == windowRecord->targetSpecific.windowHandle) ? 1 : 0); } else { // No. Just always return "has focus": PsychCopyOutDoubleArg(4, kPsychArgOptional, (double) 1); } } // Return optional valuator values: Unimplemented on OS/X. Just return an empty matrix. // The buttonArray is just a dummy assignment without any meaning. PsychCopyOutDoubleMatArg(5, kPsychArgOptional, (int) 1, (int) 0, (int) 1, buttonArray); PsychCopyOutDoubleMatArg(6, kPsychArgOptional, (int) 1, (int) 0, (int) 1, buttonArray); #endif #if PSYCH_SYSTEM == PSYCH_WINDOWS static unsigned char disabledKeys[256]; static unsigned char firsttime = 1; int keysdown, i, priorityLevel; unsigned char keyState[256]; double* buttonArray; double numButtons, timestamp; PsychNativeBooleanType* buttonStates; POINT point; HANDLE currentProcess; DWORD oldPriority = NORMAL_PRIORITY_CLASS; const DWORD realtime_class = REALTIME_PRIORITY_CLASS; PsychWindowRecordType *windowRecord; PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; // Retrieve optional number of mouse buttons: numButtons = 0; PsychCopyInDoubleArg(1, FALSE, &numButtons); // Are we operating in 'GetMouseHelper' mode? numButtons>=0 indicates this. if (numButtons>=0) { // GetMouse-Mode: Return mouse button states and mouse cursor position: PsychAllocOutDoubleMatArg(3, kPsychArgOptional, (int)1, (int)3, (int)1, &buttonArray); // Query and return mouse button state: PsychGetMouseButtonState(buttonArray); // Query and return cursor position in global coordinates: GetCursorPos(&point); PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) point.x); PsychCopyOutDoubleArg(2, kPsychArgOptional, (double) point.y); // Window provided? if (PsychIsWindowIndexArg(2)) { // Yes: Check if it has focus. PsychAllocInWindowRecordArg(2, TRUE, &windowRecord); if (!PsychIsOnscreenWindow(windowRecord)) { PsychErrorExitMsg(PsychError_user, "Provided window handle isn't an onscreen window, as required."); } PsychCopyOutDoubleArg(4, kPsychArgOptional, (double) (GetForegroundWindow() == windowRecord->targetSpecific.windowHandle) ? 1 : 0); } else { // No. Just always return "has focus": PsychCopyOutDoubleArg(4, kPsychArgOptional, (double) 1); } // Return optional valuator values: Unimplemented on Windows. Just return an empty matrix. // The ×tamp is just a dummy assignment without any meaning. PsychCopyOutDoubleMatArg(5, kPsychArgOptional, (int) 1, (int) 0, (int) 1, ×tamp); PsychCopyOutDoubleMatArg(6, kPsychArgOptional, (int) 1, (int) 0, (int) 1, buttonArray); } else { // 'KeyboardHelper' mode: We implement either KbCheck() or KbWait() via X11. // This is a hack to provide keyboard queries until a PsychHID() implementation // for Microsoft Windows is available... // Special codes -10 to -15? --> Console keyboard queries: if(numButtons <= -10 && numButtons >= -15) { ConsoleInputHelper((int) numButtons); return(PsychError_none); } if (firsttime) { // First time init: firsttime = 0; memset(keyState, 0, sizeof(keyState)); memset(disabledKeys, 0, sizeof(disabledKeys)); // These keycodes are always disabled: 0, 255: disabledKeys[0]=1; disabledKeys[255]=1; // Mouse buttone (left, right, middle) are also disabled by default: disabledKeys[1]=1; disabledKeys[2]=1; disabledKeys[4]=1; } if (numButtons==-1 || numButtons==-2) { // KbCheck()/KbWait() mode do { // Reset overall key state to "none pressed": keysdown=0; // Request current time of query: PsychGetAdjustedPrecisionTimerSeconds(×tamp); // Query state of all keys: for(i=1;i<255;i++){ keyState[i] = (GetAsyncKeyState(i) & -32768) ? 1 : 0; } // Disable all keys that are registered in disabledKeys. Check if // any non-disabled key is down. for (i=0; i<256; i++) { if (disabledKeys[i]>0) keyState[i] = 0; keysdown+=(unsigned int) keyState[i]; } // We repeat until any key pressed if in KbWait() mode, otherwise we // exit the loop after first iteration in KbCheck mode. if ((numButtons==-1) || ((numButtons==-2) && (keysdown>0))) break; // Sleep for a millisecond before next KbWait loop iteration: PsychWaitIntervalSeconds(0.001); } while(1); if (numButtons==-2) { // KbWait mode: Copy out time value. PsychCopyOutDoubleArg(1, kPsychArgOptional, timestamp); } else { // KbCheck mode: // Copy out overall keystate: PsychCopyOutDoubleArg(1, kPsychArgOptional, (keysdown>0) ? 1 : 0); // Copy out timestamp: PsychCopyOutDoubleArg(2, kPsychArgOptional, timestamp); // Copy out keyboard state: PsychAllocOutBooleanMatArg(3, kPsychArgOptional, 1, 256, 1, &buttonStates); // Build 256 elements return vector: for(i=0; i<255; i++) { buttonStates[i] = (PsychNativeBooleanType)((keyState[i+1]) ? 1 : 0); } // Special case: Null out last element: buttonStates[255] = (PsychNativeBooleanType) 0; } } if (numButtons==-3) { // Priority() - helper mode: The 2nd argument is the priority level: // Determine our processID: currentProcess = GetCurrentProcess(); // Get current scheduling policy: oldPriority = GetPriorityClass(currentProcess); // Map to PTB's scheme: switch(oldPriority) { case NORMAL_PRIORITY_CLASS: priorityLevel = 0; break; case HIGH_PRIORITY_CLASS: priorityLevel = 1; break; case REALTIME_PRIORITY_CLASS: priorityLevel = 2; break; default: priorityLevel = 0; } // Copy it out as optional return argument: PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) priorityLevel); // Query if a new level should be set: priorityLevel = -1; PsychCopyInIntegerArg(2, kPsychArgOptional, &priorityLevel); // Priority level provided? if (priorityLevel > -1) { // Map to new scheduling class: if (priorityLevel > 2) PsychErrorExitMsg(PsychErorr_argumentValueOutOfRange, "Invalid Priority level: Requested Priority() level must not exceed 2."); switch(priorityLevel) { case 0: // Standard scheduling: SetPriorityClass(currentProcess, NORMAL_PRIORITY_CLASS); // Disable any MMCSS scheduling for us: PsychSetThreadPriority((psych_thread*) 0x1, 0, 0); break; case 1: // High priority scheduling: SetPriorityClass(currentProcess, HIGH_PRIORITY_CLASS); // Additionally try to schedule us MMCSS: This will lift us roughly into the // same scheduling range as REALTIME_PRIORITY_CLASS, even if we are non-admin users // on Vista and Windows-7 and later, however with a scheduler safety net applied. PsychSetThreadPriority((psych_thread*) 0x1, 10, 0); break; case 2: // Realtime scheduling: // This can fail if Matlab is not running under a user account with proper permissions: if ((0 == SetPriorityClass(currentProcess, REALTIME_PRIORITY_CLASS)) || (REALTIME_PRIORITY_CLASS != GetPriorityClass(currentProcess))) { // Failed to get RT-Scheduling. Let's try at least high priority scheduling: SetPriorityClass(currentProcess, HIGH_PRIORITY_CLASS); // Additionally try to schedule us MMCSS: This will lift us roughly into the // same scheduling range as REALTIME_PRIORITY_CLASS, even if we are non-admin users // on Vista and Windows-7 and later, however with a scheduler safety net applied. PsychSetThreadPriority((psych_thread*) 0x1, 10, 0); } break; } } // End of Priority() helper for Win32. } } #endif #if PSYCH_SYSTEM == PSYCH_LINUX double myvaluators[100]; int numvaluators; unsigned char keys_return[32]; char* keystring; PsychGenericScriptType *kbNames; CGDirectDisplayID dpy; Window rootwin, childwin, mywin; int i, j, mx, my, dx, dy; double mxd, myd, dxd, dyd; unsigned int mask_return; double timestamp; int numButtons; double* buttonArray; PsychNativeBooleanType* buttonStates; int keysdown; XEvent event_return; XKeyPressedEvent keypressevent; int screenNumber; int priorityLevel; struct sched_param schedulingparam; PsychWindowRecordType *windowRecord; int mouseIndex; XIButtonState buttons_return; XIModifierState modifiers_return; XIGroupState group_return; PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychCopyInIntegerArg(1, kPsychArgRequired, &numButtons); // Retrieve optional screenNumber argument: if (numButtons!=-5) { screenNumber = 0; if (PsychIsScreenNumberArg(2)) { PsychCopyInScreenNumberArg(2, FALSE, &screenNumber); } // Map screenNumber to X11 display handle and screenid: PsychGetCGDisplayIDFromScreenNumber(&dpy, screenNumber); if (PsychIsWindowIndexArg(2)) { PsychAllocInWindowRecordArg(2, TRUE, &windowRecord); if (!PsychIsOnscreenWindow(windowRecord)) { PsychErrorExitMsg(PsychError_user, "Provided window handle isn't an onscreen window, as required."); } screenNumber = windowRecord->screenNumber; mywin = windowRecord->targetSpecific.xwindowHandle; // Map screenNumber to X11 display handle and screenid: PsychGetCGDisplayIDFromScreenNumber(&dpy, screenNumber); } else { mywin = RootWindow(dpy, PsychGetXScreenIdForScreen(screenNumber)); } } // Default to "old school" mouse query - System default mouse via X core protocol: mouseIndex = -1; PsychCopyInIntegerArg(3, FALSE, &mouseIndex); // Are we operating in 'GetMouseHelper' mode? numButtons>=0 indicates this. if (numButtons>=0) { // Mouse pointer query mode: numvaluators = 0; if (mouseIndex >= 0) { // XInput-2 query for handling of multiple mouse pointers: // Query input device list for screen: int nDevices; XIDeviceInfo* indevs = PsychGetInputDevicesForScreen(screenNumber, &nDevices); // Sanity check: if (NULL == indevs) PsychErrorExitMsg(PsychError_user, "Sorry, your system does not support individual mouse pointer queries."); if (mouseIndex >= nDevices) PsychErrorExitMsg(PsychError_user, "Invalid 'mouseIndex' provided. No such device."); if ((indevs[mouseIndex].use != XIMasterPointer) && (indevs[mouseIndex].use != XISlavePointer) && (indevs[mouseIndex].use != XIFloatingSlave)) { PsychErrorExitMsg(PsychError_user, "Invalid 'mouseIndex' provided. Not a pointer device."); } // We requery the device info struct to retrieve updated live device state: // Crucial for slave pointers to get any state at all, but also needed on // master pointers to get the state of additional valuators, e.g., pen pressure, // touch area, tilt etc. for digitizer tablets, touch pads etc. For master pointers, // the primary 2 axis for 2D (x,y) position and the button/modifier state will be // queried via a dedicated XIQueryPointer() call, so that info gets overriden. indevs = XIQueryDevice(dpy, indevs[mouseIndex].deviceid, &numButtons); modifiers_return.effective = 0; // Query real number of mouse buttons and the raw button and axis state // stored inside the device itself. This is done mostly because slave pointer // devices don't support XIQueryPointer() so we get their relevant info from the // XIDeviceInfo struct itself: numButtons = 0; numvaluators = 0; memset(myvaluators, 0, sizeof(myvaluators)); if (PsychIsArgPresent(PsychArgOut, 6)) { // Usercode wants valuator info structs: for (i = 0; i < indevs->num_classes; i++) if (indevs->classes[i]->type == XIValuatorClass) numIValuators++; PsychAllocOutStructArray(6, TRUE, numIValuators, numValuatorStructFieldNames, valuatorInfo, &valuatorStruct); } for (i = 0; i < indevs->num_classes; i++) { // printf("Class %i: Type %i\n", i, (int) indevs->classes[i]->type); if (indevs->classes[i]->type == XIButtonClass) { // Number of buttons: For all pointers. numButtons = ((XIButtonClassInfo*) indevs->classes[i])->num_buttons; // Button state for slave pointers. Will get overriden for master pointers: buttons_return.mask = ((XIButtonClassInfo*) indevs->classes[i])->state.mask; buttons_return.mask_len = ((XIButtonClassInfo*) indevs->classes[i])->state.mask_len; } // Axis state for slave pointers. First two axis (x,y) will get overriden for master pointers: if (indevs->classes[i]->type == XIValuatorClass) { XIValuatorClassInfo* axis = (XIValuatorClassInfo*) indevs->classes[i]; if (axis->number == 0) mxd = axis->value; // x-Axis. if (axis->number == 1) myd = axis->value; // y-Axis. // Additional axis, e.g., digitizer tablet, touchpads etc.: if (axis->number >= 0 && axis->number < 100) { myvaluators[axis->number] = axis->value; numvaluators = (numvaluators >= axis->number + 1) ? numvaluators : axis->number + 1; } // Assign valuator info struct, if requested: if (valuatorStruct) { if (axis->label != None) { char* atomlabel = XGetAtomName(dpy, axis->label); PsychSetStructArrayStringElement("label", axis->number, atomlabel, valuatorStruct); XFree(atomlabel); } else { PsychSetStructArrayStringElement("label", axis->number, "None", valuatorStruct); } PsychSetStructArrayDoubleElement("min", axis->number, (double) axis->min, valuatorStruct); PsychSetStructArrayDoubleElement("max", axis->number, (double) axis->max, valuatorStruct); PsychSetStructArrayDoubleElement("resolution", axis->number, (double) axis->resolution, valuatorStruct); PsychSetStructArrayDoubleElement("mode", axis->number, (double) axis->mode, valuatorStruct); PsychSetStructArrayDoubleElement("sourceID", axis->number, (double) axis->sourceid, valuatorStruct); } // printf("AXIS %i, LABEL = %s, MIN = %f, MAX = %f, VAL = %f\n", axis->number, (char*) "NONE", (float) axis->min, (float) axis->max, (float) axis->value); } } // Add 32 buttons for modifier key state vector: numButtons += 32; // A real master pointer: Use official query for mouse devices. if (indevs->use == XIMasterPointer) { // Query pointer location and state: XIQueryPointer(dpy, indevs->deviceid, RootWindow(dpy, PsychGetXScreenIdForScreen(screenNumber)), &rootwin, &childwin, &mxd, &myd, &dxd, &dyd, &buttons_return, &modifiers_return, &group_return); } // Copy out mouse x and y position: PsychCopyOutDoubleArg(1, kPsychArgOptional, mxd); PsychCopyOutDoubleArg(2, kPsychArgOptional, myd); // Copy out mouse button state: PsychAllocOutDoubleMatArg(3, kPsychArgOptional, (int)1, (int) numButtons, (int)1, &buttonArray); memset(buttonArray, 0, sizeof(double) * numButtons); if (numButtons > 0) { // Mouse buttons: const int buttonOffset = 1; // Buttons start at bit 1, not 0 for some strange reason? At least so on Ubuntu 10.10 and 11.10 with 2 mice and 1 joystick? for (i = buttonOffset; (i < numButtons - 32) && ((i / 8 ) < buttons_return.mask_len); i++) { buttonArray[i - buttonOffset] = (double) ((buttons_return.mask[i / 8] & (1 << (i % 8))) ? 1 : 0); } // Free mask if retrieved via XIQueryPointer(): if (indevs->use == XIMasterPointer) free(buttons_return.mask); // Append modifier key state from associated master keyboard. Last 32 entries: for (i = 0; i < 32; i++) { buttonArray[numButtons - 32 + i] = (double) ((modifiers_return.effective & (1 << i)) ? 1 : 0); } } // Release live state info structure: XIFreeDeviceInfo(indevs); } else { // Old school core protocol query of virtual core pointer: XQueryPointer(dpy, RootWindow(dpy, PsychGetXScreenIdForScreen(screenNumber)), &rootwin, &childwin, &mx, &my, &dx, &dy, &mask_return); // Copy out mouse x and y position: PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) mx); PsychCopyOutDoubleArg(2, kPsychArgOptional, (double) my); // Copy out mouse button state: PsychAllocOutDoubleMatArg(3, kPsychArgOptional, (int)1, (int)numButtons, (int)1, &buttonArray); // Bits 8, 9 and 10 of mask_return seem to correspond to mouse buttons // 1, 2 and 3 of a mouse for some weird reason. Bits 0-7 describe keyboard modifier keys // like Alt, Ctrl, Shift, ScrollLock, NumLock, CapsLock... // We remap here, so the first three returned entries correspond to the mouse buttons and // the rest is attached behind, if requested... // Mouse buttons: Left, Middle, Right == 0, 1, 2, aka 1,2,3 in Matlab space... for (i=0; i<numButtons && i<3; i++) { buttonArray[i] = (mask_return & (1<<(i+8))) ? 1 : 0; } // Modifier keys 0 to 7 appended: for (i=3; i<numButtons && i<3+8; i++) { buttonArray[i] = (mask_return & (1<<(i-3))) ? 1 : 0; } // Everything else appended: for (i=11; i<numButtons; i++) { buttonArray[i] = (mask_return & (1<<i)) ? 1 : 0; } } // Return optional 4th argument: Focus state. Returns 1 if our window has // keyboard input focus, zero otherwise: XGetInputFocus(dpy, &rootwin, &i); PsychCopyOutDoubleArg(4, kPsychArgOptional, (double) (rootwin == mywin) ? 1 : 0); // Return optional valuator values: PsychCopyOutDoubleMatArg(5, kPsychArgOptional, (int) 1, (int) numvaluators, (int) 1, &myvaluators[0]); } else { // 'KeyboardHelper' mode: We implement either KbCheck() or KbWait() via X11. // This is a hack to provide keyboard queries until a PsychHID() implementation // for Linux is available... // Special codes -10 to -15? --> Console keyboard queries: if(numButtons <= -10 && numButtons >= -15) { ConsoleInputHelper((int) numButtons); return(PsychError_none); } if (numButtons==-1 || numButtons==-2) { // KbCheck()/KbWait() mode: // Switch X-Server into synchronous mode: We need this to get // a higher timing precision. XSynchronize(dpy, TRUE); do { // Reset overall key state to "none pressed": keysdown=0; // Request current keyboard state from X-Server: XQueryKeymap(dpy, keys_return); // Request current time of query: PsychGetAdjustedPrecisionTimerSeconds(×tamp); // Any key down? for (i=0; i<32; i++) keysdown+=(unsigned int) keys_return[i]; // We repeat until any key pressed if in KbWait() mode, otherwise we // exit the loop after first iteration in KbCheck mode. if ((numButtons==-1) || ((numButtons==-2) && (keysdown>0))) break; // Sleep for a few milliseconds before next KbWait loop iteration: PsychWaitIntervalSeconds(0.01); } while(1); if (numButtons==-2) { // Copy out time: PsychCopyOutDoubleArg(1, kPsychArgOptional, timestamp); } else { // KbCheck mode: // Copy out overall keystate: PsychCopyOutDoubleArg(1, kPsychArgOptional, (keysdown>0) ? 1 : 0); // copy out timestamp: PsychCopyOutDoubleArg(2, kPsychArgOptional, timestamp); // Copy keyboard state: PsychAllocOutBooleanMatArg(3, kPsychArgOptional, 1, 256, 1, &buttonStates); // Map 32 times 8 bitvector to 256 element return vector: for(i=0; i<32; i++) { for(j=0; j<8; j++) { buttonStates[i*8 + j] = (PsychNativeBooleanType)(keys_return[i] & (1<<j)) ? 1 : 0; } } } } else if (numButtons == -3) { // numButtons == -3 --> KbName mapping mode: // Return the full keyboard keycode to ASCII character code mapping table... PsychAllocOutCellVector(1, kPsychArgOptional, 256, &kbNames); for(i=0; i<256; i++) { // Map keyboard scan code to KeySym: keystring = XKeysymToString(XKeycodeToKeysym(dpy, i, 0)); if (keystring) { // Character found: Return its ASCII name string: PsychSetCellVectorStringElement(i, keystring, kbNames); } else { // No character for this keycode: PsychSetCellVectorStringElement(i, "", kbNames); } } } else if (numButtons == -4) { // GetChar() emulation. /* do { */ /* // Fetch next keypress event from queue, block if none is available... */ /* keystring = NULL; */ /* XNextEvent(dpy, &event_return); */ /* // Check for valid keypress event and extract character: */ /* if (event_return.type == KeyPress) { */ /* keypressevent = (XKeyPressedEvent) event_return; */ /* keystring = NULL; */ /* keystring = XKeysymToString(XKeycodeToKeysym(dpy, keypressevent.keycode, 0)); */ /* } */ /* // Repeat until a valid char is returned. */ /* } while (keystring == NULL); */ /* // Copy out character: */ /* PsychCopyOutCharArg(1, kPsychArgOptional, (char) keystring); */ /* // Copy out time: */ /* PsychCopyOutDoubleArg(2, kPsychArgOptional, (double) keypressevent.time); */ } else if (numButtons==-5) { // Priority() - helper mode: The 2nd argument is the priority level: // Query scheduling policy and priority: pthread_getschedparam(pthread_self(), &priorityLevel, &schedulingparam); // If scheduling mode is a realtime mode (RoundRobin realtime RR, or FIFO realtime), // then assign RT priority level (range 1-99) as current priorityLevel, otherwise // assign non realtime priority level zero: priorityLevel = (priorityLevel == SCHED_RR || priorityLevel == SCHED_FIFO) ? schedulingparam.sched_priority : 0; // Copy it out as optional return argument: PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) priorityLevel); // Query if a new level should be set: priorityLevel = -1; PsychCopyInIntegerArg(2, kPsychArgOptional, &priorityLevel); errno=0; // Priority level provided? if (priorityLevel > -1) { // Map to new scheduling class: if (priorityLevel > 99 || priorityLevel < 0) PsychErrorExitMsg(PsychErorr_argumentValueOutOfRange, "Invalid Priority level: Requested Priority() level must be between zero and 99!"); if (priorityLevel > 0) { // Realtime FIFO scheduling and all pages of Matlab/Octave locked into memory: schedulingparam.sched_priority = priorityLevel; priorityLevel = pthread_setschedparam(pthread_self(), SCHED_FIFO, &schedulingparam); if (priorityLevel == -1) { // Failed! if(!PsychPrefStateGet_SuppressAllWarnings()) { printf("PTB-ERROR: Failed to enable realtime-scheduling with Priority(%i) [%s]!\n", schedulingparam.sched_priority, strerror(errno)); if (errno==EPERM) { printf("PTB-ERROR: You need to run Matlab/Octave with root-privileges, or run the script PsychLinuxConfiguration once for this to work.\n"); } } errno=0; } else { // RT-Scheduling active. Lock all current and future memory: priorityLevel = mlockall(MCL_CURRENT | MCL_FUTURE); if (priorityLevel!=0) { // Failed! Report problem as warning, but don't worry further. if(!PsychPrefStateGet_SuppressAllWarnings()) printf("PTB-WARNING: Failed to enable system memory locking with Priority(%i) [%s]!\n", schedulingparam.sched_priority, strerror(errno)); // Undo any possibly partial mlocks.... munlockall(); errno=0; } } } else { // Standard scheduling and no memory locking: schedulingparam.sched_priority = 0; priorityLevel = pthread_setschedparam(pthread_self(), SCHED_OTHER, &schedulingparam); if (priorityLevel == -1) { // Failed! if(!PsychPrefStateGet_SuppressAllWarnings()) { printf("PTB-ERROR: Failed to disable realtime-scheduling with Priority(%i) [%s]!\n", schedulingparam.sched_priority, strerror(errno)); if (errno==EPERM) { printf("PTB-ERROR: You need to run Matlab/Octave with root-privileges, or run the script PsychLinuxConfiguration once for this to work.\n"); } } errno=0; } munlockall(); errno=0; } // End of setup of new Priority... } // End of Priority() helper for Linux. } } // End of special functions handling for Linux... #endif return(PsychError_none); }
void handleDeviceChange() { int n; XIDeviceInfo *info = XIQueryDevice(display, XIAllDevices, &n); if (!info) { printf("No XInput devices available\n"); exit(1); } int d; for(d = 0; d < profiles.nDeviceSettings; d++) { profiles.deviceSettings[d].inputDeviceCount = 0; } /* Go through input devices and add to matching profile */ int i; for (i = 0; i < n; i++) { if (info[i].use == XIMasterPointer || info[i].use == XIMasterKeyboard) { } else { int foundProfile = FALSE; for(d = 0; d < profiles.nDeviceSettings; d++) { if (profiles.deviceSettings[d].inputDeviceName != NULL && !strcmp(info[i].name, profiles.deviceSettings[d].inputDeviceName)) { if(debugMode) { printf("Device %s for profile %s found with ID %i\n", info[i].name, profiles.deviceSettings[d].inputDeviceName, info[i].deviceid); } if(profiles.deviceSettings[d].inputDeviceCount < MAX_DEVICES_PER_PROFILE) { profiles.deviceSettings[d].inputDeviceCount++; profiles.deviceSettings[d].inputDeviceIDs[profiles.deviceSettings[d].inputDeviceCount-1] = info[i].deviceid; if(profiles.deviceSettings[d].autoCalibration) { /* Set default calibration from axes */ setAutoCalibrationData(d, &(info[i])); } } foundProfile = TRUE; break; } } if(foundProfile == FALSE) { /* No profile available. If touchscreen, create dummy profile */ if(isAbsoluteInputDevice(&(info[i]))) { if(debugMode) { printf("Found absolute X and Y axis on device %i, assume it's a touchscreen.\n", info[i].deviceid); printf("No profile found for it, create dummy profile.\n"); } /* Create dummy profile */ char *deviceName = malloc((strlen(info[i].name) + 1) * sizeof (char)); strcpy(deviceName, info[i].name); addDeviceSettings(&profiles, deviceName, NULL, TRUE, TRUE, 0, 0, 0, 0, 0); profiles.deviceSettings[profiles.nDeviceSettings-1].inputDeviceCount = 1; profiles.deviceSettings[profiles.nDeviceSettings-1].inputDeviceIDs[0] = info[i].deviceid; /* Set default calibration from axes */ setAutoCalibrationData(profiles.nDeviceSettings - 1, &(info[i])); } } } } XIFreeDeviceInfo(info); handleDisplayChange((XRRScreenChangeNotifyEvent*) NULL); }
/* Main function, contains kernel driver event loop */ int main(int argc, char **argv) { char* devname = 0; int doDaemonize = 1; int doWait = 0; int clickMode = 2; int i; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--debug") == 0) { doDaemonize = 0; debugMode = 1; } else if (strcmp(argv[i], "--wait") == 0) { doWait = 1; } else if (strcmp(argv[i], "--click=first") == 0) { clickMode = 0; } else if (strcmp(argv[i], "--click=second") == 0) { clickMode = 1; } else if (strcmp(argv[i], "--click=center") == 0) { clickMode = 2; } else { devname = argv[i]; } } initGestures(clickMode); if (doDaemonize) { daemonize(); } if (doWait) { /* Wait until all necessary things are loaded */ sleep(10); } /* Connect to X server */ if ((display = XOpenDisplay(NULL)) == NULL) { fprintf(stderr, "Couldn't connect to X server\n"); exit(1); } /* Read X data */ screenNum = DefaultScreen(display); root = RootWindow(display, screenNum); // realDisplayWidth = DisplayWidth(display, screenNum); // realDisplayHeight = DisplayHeight(display, screenNum); WM_CLASS = XInternAtom(display, "WM_CLASS", 0); /* Get notified about new windows */ XSelectInput(display, root, StructureNotifyMask | SubstructureNotifyMask); //TODO load blacklist and profiles from file(s) /* Device file name */ if (devname == 0) { devname = "/dev/twofingtouch"; } /* Try to read from device file */ int fileDesc; if ((fileDesc = open(devname, O_RDONLY)) < 0) { perror("twofing"); return 1; } fd_set fileDescSet; FD_ZERO(&fileDescSet); int eventQueueDesc = XConnectionNumber(display); while (1) { /* Perform initialization at beginning and after module has been re-loaded */ int rd, i; struct input_event ev[64]; char name[256] = "Unknown"; /* Read device name */ ioctl(fileDesc, EVIOCGNAME(sizeof(name)), name); printf("Input device name: \"%s\"\n", name); //TODO activate again? //XSetErrorHandler(invalidWindowHandler); int opcode, event, error; if (!XQueryExtension(display, "RANDR", &opcode, &event, &error)) { printf("X RANDR extension not available.\n"); XCloseDisplay(display); exit(1); } /* Which version of XRandR? We support 1.3 */ int major = 1, minor = 3; if (!XRRQueryVersion(display, &major, &minor)) { printf("XRandR version not available.\n"); XCloseDisplay(display); exit(1); } else if(!(major>1 || (major == 1 && minor >= 3))) { printf("XRandR 1.3 not available. Server supports %d.%d\n", major, minor); XCloseDisplay(display); exit(1); } /* XInput Extension available? */ if (!XQueryExtension(display, "XInputExtension", &opcode, &event, &error)) { printf("X Input extension not available.\n"); XCloseDisplay(display); exit(1); } /* Which version of XI2? We support 2.1 */ major = 2; minor = 1; if (XIQueryVersion(display, &major, &minor) == BadRequest) { printf("XI 2.1 not available. Server supports %d.%d\n", major, minor); XCloseDisplay(display); exit(1); } screenWidth = XDisplayWidth(display, screenNum); screenHeight = XDisplayHeight(display, screenNum); int n; XIDeviceInfo *info = XIQueryDevice(display, XIAllDevices, &n); if (!info) { printf("No XInput devices available\n"); exit(1); } /* Go through input devices and look for that with the same name as the given device */ int devindex; for (devindex = 0; devindex < n; devindex++) { if (info[devindex].use == XIMasterPointer || info[devindex].use == XIMasterKeyboard) continue; if (strcmp(info[devindex].name, name) == 0) { deviceID = info[devindex].deviceid; break; } } if (deviceID == -1) { printf("Input device not found in XInput device list.\n"); exit(1); } XIFreeDeviceInfo(info); if(debugMode) printf("XInput device id is %i.\n", deviceID); /* Prepare by reading calibration */ readCalibrationData(1); /* Receive device property change events */ XIEventMask device_mask2; unsigned char mask_data2[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; device_mask2.deviceid = deviceID; device_mask2.mask_len = sizeof(mask_data2); device_mask2.mask = mask_data2; XISetMask(device_mask2.mask, XI_PropertyEvent); XISetMask(device_mask2.mask, XI_ButtonPress); //XISetMask(device_mask2.mask, XI_TouchBegin); //XISetMask(device_mask2.mask, XI_TouchUpdate); //XISetMask(device_mask2.mask, XI_TouchEnd); XISelectEvents(display, root, &device_mask2, 1); /* Recieve events when screen size changes */ XRRSelectInput(display, root, RRScreenChangeNotifyMask); /* Receive touch events */ /* Needed for XTest to work correctly */ XTestGrabControl(display, True); /* Needed for some reason to receive events */ /* XGrabPointer(display, root, False, 0, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); XUngrabPointer(display, CurrentTime);*/ grab(display, deviceID); printf("Reading input from device ... (interrupt to exit)\n"); /* We perform raw event reading here as X touch events don't seem too reliable */ int currentSlot = 0; /* If we use the legacy protocol, we collect all data of one finger into tempFingerInfo and set it to the correct slot once MT_SYNC occurs. */ FingerInfo tempFingerInfo = { .rawX=0, .rawY=0, .rawZ=0, .id = -1, .slotUsed = 0, .setThisTime = 0 }; while (1) { FD_SET(fileDesc, &fileDescSet); FD_SET(eventQueueDesc, &fileDescSet); select(MAX(fileDesc, eventQueueDesc) + 1, &fileDescSet, NULL, NULL, getEasingStepTimeVal()); checkEasingStep(); if(FD_ISSET(fileDesc, &fileDescSet)) { rd = read(fileDesc, ev, sizeof(struct input_event) * 64); if (rd < (int) sizeof(struct input_event)) { printf("Data stream stopped\n"); break; } for (i = 0; i < rd / sizeof(struct input_event); i++) { if (ev[i].type == EV_SYN) { if (0 == ev[i].code) { // Ev_Sync event end /* All finger data received, so process now. */ if(useLegacyProtocol) { /* Clear slots not set this time */ int i; for(i = 0; i < 2; i++) { if(fingerInfos[i].setThisTime) { fingerInfos[i].setThisTime = 0; } else { /* Clear slot */ fingerInfos[i].slotUsed = 0; } } tempFingerInfo.slotUsed = 0; } processFingers(); } else if (2 == ev[i].code) { // MT_Sync : Multitouch event end if(!useLegacyProtocol) { /* This messsage indicates we use legacy protocol, so switch */ useLegacyProtocol = 1; currentSlot = -1; tempFingerInfo.slotUsed = 0; if(debugMode) printf("Switch to legacy protocol.\n"); } else { if(tempFingerInfo.slotUsed) { /* Finger info for one finger collected in tempFingerInfo, so save it to fingerInfos. */ /* Look for slot to put the data into by looking at the tracking ids */ int index = -1; int i; for(i = 0; i < 2; i++) { if(fingerInfos[i].slotUsed && fingerInfos[i].id == tempFingerInfo.id) { index = i; break; } } if(index == -1) { for(i = 0; i < 2; i++) { if(!fingerInfos[i].slotUsed) { /* "Empty" slot, so we can add it. */ index = i; fingerInfos[i].id = tempFingerInfo.id; fingerInfos[i].slotUsed = 1; break; } } } if(index != -1) { /* Copy temporary data to slot */ fingerInfos[index].setThisTime = 1; fingerInfos[index].rawX = tempFingerInfo.rawX; fingerInfos[index].rawY = tempFingerInfo.rawY; fingerInfos[index].rawZ = tempFingerInfo.rawZ; } } } } } else if (ev[i].type == EV_MSC && (ev[i].code == MSC_RAW || ev[i].code == MSC_SCAN)) { } else if (ev[i].code == 47) { currentSlot = ev[i].value; if(currentSlot < 0 || currentSlot > 1) currentSlot = -1; } else { /* Set finger info values for current finger */ if (ev[i].code == 57) { /* ABS_MT_TRACKING_ID */ if(currentSlot != -1) { if(ev[i].value == -1) { fingerInfos[currentSlot].slotUsed = 0; } else { fingerInfos[currentSlot].id = ev[i].value; fingerInfos[currentSlot].slotUsed = 1; } } else if(useLegacyProtocol) { tempFingerInfo.id = ev[i].value; tempFingerInfo.slotUsed = 1; } }; if (ev[i].code == 53) { if(currentSlot != -1) { fingerInfos[currentSlot].rawX = ev[i].value; } else if(useLegacyProtocol) { tempFingerInfo.rawX = ev[i].value; } }; if (ev[i].code == 54) { if(currentSlot != -1) { fingerInfos[currentSlot].rawY = ev[i].value; } else if(useLegacyProtocol) { tempFingerInfo.rawY = ev[i].value; } }; if (ev[i].code == 58) { if(currentSlot != -1) { fingerInfos[currentSlot].rawZ = ev[i].value; } else if(useLegacyProtocol) { tempFingerInfo.rawZ = ev[i].value; } }; } } } if(FD_ISSET(eventQueueDesc, &fileDescSet)) { handleXEvent(); } } /* Stream stopped, probably because module has been unloaded */ close(fileDesc); /* Clean up */ releaseButton(); ungrab(display, deviceID); /* Wait until device file is there again */ while ((fileDesc = open(devname, O_RDONLY)) < 0) { sleep(1); } } }
/** * Remove a master device. * By default, all attached devices are set to Floating, unless parameters are * given. */ int remove_master(Display* dpy, int argc, char** argv, char *name, char *desc) { XIRemoveMasterInfo r; XIDeviceInfo *info; int ret; if (argc == 0) { fprintf(stderr, "usage: xinput %s %s\n", name, desc); return EXIT_FAILURE; } info = xi2_find_device_info(dpy, argv[0]); if (!info) { fprintf(stderr, "unable to find device %s\n", argv[0]); return EXIT_FAILURE; } r.type = XIRemoveMaster; r.deviceid = info->deviceid; if (argc >= 2) { if (!strcmp(argv[1], "Floating")) r.return_mode = XIFloating; else if (!strcmp(argv[1], "AttachToMaster")) r.return_mode = XIAttachToMaster; else Error(BadValue, "Invalid return_mode.\n"); } else r.return_mode = XIFloating; if (r.return_mode == XIAttachToMaster) { r.return_pointer = 0; if (argc >= 3) { info = xi2_find_device_info(dpy, argv[2]); if (!info) { fprintf(stderr, "unable to find device %s\n", argv[2]); return EXIT_FAILURE; } r.return_pointer = info->deviceid; } r.return_keyboard = 0; if (argc >= 4) { info = xi2_find_device_info(dpy, argv[3]); if (!info) { fprintf(stderr, "unable to find device %s\n", argv[3]); return EXIT_FAILURE; } r.return_keyboard = info->deviceid; } if (!r.return_pointer || !r.return_keyboard) { int i, ndevices; info = XIQueryDevice(dpy, XIAllMasterDevices, &ndevices); for(i = 0; i < ndevices; i++) { if (info[i].use == XIMasterPointer && !r.return_pointer) r.return_pointer = info[i].deviceid; if (info[i].use == XIMasterKeyboard && !r.return_keyboard) r.return_keyboard = info[i].deviceid; if (r.return_pointer && r.return_keyboard) break; } XIFreeDeviceInfo(info); } } ret = XIChangeHierarchy(dpy, (XIAnyHierarchyChangeInfo*)&r, 1); return ret; }