/* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2004-2006 Adrian Page * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details.g * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Some of the X11-specific event handling code is based upon code from src/kernel/qapplication_x11.cpp from the TQt GUI Toolkit and is subject to the following license and copyright: **************************************************************************** ** ** ** Implementation of X11 startup routines and event handling ** ** Created : 931029 ** ** Copyright (C) 1992-2003 Trolltech AS. All rights reserved. ** ** This file is part of the kernel module of the TQt GUI Toolkit. ** ** This file may be distributed under the terms of the Q Public License ** as defined by Trolltech AS of Norway and appearing in the file ** LICENSE.TQPL included in the packaging of this file. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** Licensees holding valid TQt Enterprise Edition or TQt Professional Edition ** licenses for Unix/X11 may use this file in accordance with the TQt Commercial ** License Agreement provided with the Software. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for ** information about TQt Commercial License Agreements. ** See http://www.trolltech.com/qpl/ for TQPL licensing information. ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #include #include "kis_canvas.h" #include "kis_cursor.h" #include "kis_move_event.h" #include "kis_button_press_event.h" #include "kis_button_release_event.h" #include "kis_double_click_event.h" #include "kis_config.h" #include "kis_qpaintdevice_canvas.h" #include "kis_opengl_canvas.h" #include "kis_config.h" #include "kis_input_device.h" #include "fixx11h.h" #ifdef Q_WS_X11 #include #include #include #include bool KisCanvasWidget::X11SupportInitialised = false; long KisCanvasWidget::X11AltMask = 0; long KisCanvasWidget::X11MetaMask = 0; #if defined(EXTENDED_X11_TABLET_SUPPORT) int KisCanvasWidget::X11DeviceMotionNotifyEvent = -1; int KisCanvasWidget::X11DeviceButtonPressEvent = -1; int KisCanvasWidget::X11DeviceButtonReleaseEvent = -1; int KisCanvasWidget::X11ProximityInEvent = -1; int KisCanvasWidget::X11ProximityOutEvent = -1; //X11XIDTabletDeviceMap KisCanvasWidget::X11TabletDeviceMap; std::map KisCanvasWidget::X11TabletDeviceMap; #endif // EXTENDED_X11_TABLET_SUPPORT #endif // Q_WS_X11 KisCanvasWidget::KisCanvasWidget() { m_enableMoveEventCompressionHint = false; m_lastPressure = 0; #ifdef Q_WS_X11 if (!X11SupportInitialised) { initX11Support(); } m_lastRootX = -1; m_lastRootY = -1; #endif } KisCanvasWidget::~KisCanvasWidget() { } void KisCanvasWidget::widgetGotPaintEvent(TQPaintEvent *e) { emit sigGotPaintEvent(e); } void KisCanvasWidget::widgetGotMousePressEvent(TQMouseEvent *e) { KisButtonPressEvent ke(KisInputDevice::mouse(), KisPoint(e->pos()), KisPoint(e->globalPos()), PRESSURE_DEFAULT, 0, 0, e->button(), e->state()); buttonPressEvent(&ke); } void KisCanvasWidget::widgetGotMouseReleaseEvent(TQMouseEvent *e) { KisButtonReleaseEvent ke(KisInputDevice::mouse(), KisPoint(e->pos()), KisPoint(e->globalPos()), PRESSURE_DEFAULT, 0, 0, e->button(), e->state()); buttonReleaseEvent(&ke); } void KisCanvasWidget::widgetGotMouseDoubleClickEvent(TQMouseEvent *e) { KisDoubleClickEvent ke(KisInputDevice::mouse(), KisPoint(e->pos()), KisPoint(e->globalPos()), PRESSURE_DEFAULT, 0, 0, e->button(), e->state()); doubleClickEvent(&ke); } void KisCanvasWidget::widgetGotMouseMoveEvent(TQMouseEvent *e) { KisMoveEvent ke(KisInputDevice::mouse(), KisPoint(e->pos()), KisPoint(e->globalPos()), PRESSURE_DEFAULT, 0, 0, e->state()); moveEvent(&ke); } void KisCanvasWidget::widgetGotTabletEvent(TQTabletEvent *e) { KisInputDevice device; switch (e->device()) { default: case TQTabletEvent::NoDevice: case TQTabletEvent::Stylus: device = KisInputDevice::stylus(); break; case TQTabletEvent::Puck: device = KisInputDevice::puck(); break; case TQTabletEvent::Eraser: device = KisInputDevice::eraser(); break; } double pressure = e->pressure() / 255.0; if (e->type() == TQEvent::TabletPress) { KisButtonPressEvent ke(device, KisPoint(e->pos()), KisPoint(e->globalPos()), pressure, e->xTilt(), e->yTilt(), Qt::LeftButton, Qt::NoButton); translateTabletEvent(&ke); } else if (e->type() == TQEvent::TabletRelease) { KisButtonReleaseEvent ke(device, KisPoint(e->pos()), KisPoint(e->globalPos()), pressure, e->xTilt(), e->yTilt(), Qt::LeftButton, Qt::NoButton); translateTabletEvent(&ke); } else { KisMoveEvent ke(device, KisPoint(e->pos()), KisPoint(e->globalPos()), pressure, e->xTilt(), e->yTilt(), Qt::NoButton); translateTabletEvent(&ke); #ifdef Q_WS_X11 // Fix the problem that when you change from using a tablet device to the mouse, // the first mouse button event is not recognised. This is because we handle // X11 core mouse move events directly so TQt does not get to see them. This breaks // the tablet event accept/ignore mechanism, causing TQt to consume the first // mouse button event it sees, instead of a mouse move. 'Ignoring' tablet move events // stops TQt from stealing the next mouse button event. This does not affect the // tablet aware tools as they do not care about mouse moves while the tablet device is // drawing. e->ignore(); #endif } } void KisCanvasWidget::widgetGotEnterEvent(TQEvent *e) { emit sigGotEnterEvent(e); } void KisCanvasWidget::widgetGotLeaveEvent(TQEvent *e) { emit sigGotLeaveEvent(e); } void KisCanvasWidget::widgetGotWheelEvent(TQWheelEvent *e) { emit sigGotMouseWheelEvent(e); } void KisCanvasWidget::widgetGotKeyPressEvent(TQKeyEvent *e) { emit sigGotKeyPressEvent(e); } void KisCanvasWidget::widgetGotKeyReleaseEvent(TQKeyEvent *e) { emit sigGotKeyReleaseEvent(e); } void KisCanvasWidget::widgetGotDragEnterEvent(TQDragEnterEvent *e) { emit sigGotDragEnterEvent(e); } void KisCanvasWidget::widgetGotDropEvent(TQDropEvent *e) { emit sigGotDropEvent(e); } void KisCanvasWidget::moveEvent(KisMoveEvent *e) { emit sigGotMoveEvent(e); } void KisCanvasWidget::buttonPressEvent(KisButtonPressEvent *e) { TQWidget *widget = dynamic_cast(this); Q_ASSERT(widget != 0); if (widget) { widget->setFocus(); } emit sigGotButtonPressEvent(e); } void KisCanvasWidget::buttonReleaseEvent(KisButtonReleaseEvent *e) { emit sigGotButtonReleaseEvent(e); } void KisCanvasWidget::doubleClickEvent(KisDoubleClickEvent *e) { emit sigGotDoubleClickEvent(e); } void KisCanvasWidget::translateTabletEvent(KisEvent *e) { bool checkThresholdOnly = false; if (e->type() == KisEvent::ButtonPressEvent || e->type() == KisEvent::ButtonReleaseEvent) { KisButtonEvent *b = static_cast(e); if (b->button() == Qt::MidButton || b->button() == Qt::RightButton) { if (e->type() == KisEvent::ButtonPressEvent) { buttonPressEvent(static_cast(e)); } else { buttonReleaseEvent(static_cast(e)); } checkThresholdOnly = true; } } // Use pressure threshold to detect 'left button' press/release if (e->pressure() >= PRESSURE_THRESHOLD && m_lastPressure < PRESSURE_THRESHOLD) { KisButtonPressEvent ke(e->device(), e->pos(), e->globalPos(), e->pressure(), e->xTilt(), e->yTilt(), Qt::LeftButton, e->state()); buttonPressEvent(&ke); } else if (e->pressure() < PRESSURE_THRESHOLD && m_lastPressure >= PRESSURE_THRESHOLD) { KisButtonReleaseEvent ke(e->device(), e->pos(), e->globalPos(), e->pressure(), e->xTilt(), e->yTilt(), Qt::LeftButton, e->state()); buttonReleaseEvent(&ke); } else { if (!checkThresholdOnly) { KisMoveEvent ke(e->device(), e->pos(), e->globalPos(), e->pressure(), e->xTilt(), e->yTilt(), e->state()); moveEvent(&ke); } } m_lastPressure = e->pressure(); } #ifdef Q_WS_X11 void KisCanvasWidget::initX11Support() { if (X11SupportInitialised) { return; } X11SupportInitialised = true; Display *x11Display = TQApplication::desktop()->x11Display(); // Look at the modifier mapping and get the correct masks for alt/meta XModifierKeymap *map = XGetModifierMapping(x11Display); if (map) { int mapIndex = 0; for (int maskIndex = 0; maskIndex < 8; maskIndex++) { for (int i = 0; i < map->max_keypermod; i++) { if (map->modifiermap[mapIndex]) { KeySym sym = XkbKeycodeToKeysym(x11Display, map->modifiermap[mapIndex], 0, 0); if (X11AltMask == 0 && (sym == XK_Alt_L || sym == XK_Alt_R)) { X11AltMask = 1 << maskIndex; } if (X11MetaMask == 0 && (sym == XK_Meta_L || sym == XK_Meta_R)) { X11MetaMask = 1 << maskIndex; } } mapIndex++; } } XFreeModifiermap(map); } else { // Assume defaults X11AltMask = Mod1Mask; X11MetaMask = Mod4Mask; } #if defined(EXTENDED_X11_TABLET_SUPPORT) int numDevices = 0; const XDeviceInfo *devices = XListInputDevices(x11Display, &numDevices); if (devices != NULL) { XID lastStylusSeen = 0; XID lastEraserSeen = 0; bool foundStylus = false; bool foundEraser = false; for (int i = 0; i < numDevices; i++) { const XDeviceInfo *device = devices + i; X11TabletDevice tabletDevice(device); if (tabletDevice.mightBeTabletDevice()) { tabletDevice.readSettingsFromConfig(); TQString lowerCaseName = tabletDevice.name().lower(); // Find the devices that TQt will use as its stylus and eraser devices. if (!foundStylus || !foundEraser) { if (lowerCaseName.startsWith("stylus") || lowerCaseName.startsWith("pen")) { lastStylusSeen = device->id; foundStylus = true; } else if (lowerCaseName.startsWith("eraser")) { lastEraserSeen = device->id; foundEraser = true; } } X11TabletDeviceMap[device->id] = tabletDevice; // Event types are device-independent. Store any // the device supports. if (tabletDevice.buttonPressEvent() >= 0) { X11DeviceButtonPressEvent = tabletDevice.buttonPressEvent(); } if (tabletDevice.buttonReleaseEvent() >= 0) { X11DeviceButtonReleaseEvent = tabletDevice.buttonReleaseEvent(); } if (tabletDevice.motionNotifyEvent() >= 0) { X11DeviceMotionNotifyEvent = tabletDevice.motionNotifyEvent(); } if (tabletDevice.proximityInEvent() >= 0) { X11ProximityInEvent = tabletDevice.proximityInEvent(); } if (tabletDevice.proximityOutEvent() >= 0) { X11ProximityOutEvent = tabletDevice.proximityOutEvent(); } } } // Allocate input devices. for (X11XIDTabletDeviceMap::iterator it = X11TabletDeviceMap.begin(); it != X11TabletDeviceMap.end(); ++it) { X11TabletDevice& tabletDevice = (*it).second; if (foundStylus && tabletDevice.id() == lastStylusSeen) { tabletDevice.setInputDevice(KisInputDevice::stylus()); } else if (foundEraser && tabletDevice.id() == lastEraserSeen) { tabletDevice.setInputDevice(KisInputDevice::eraser()); } else { tabletDevice.setInputDevice(KisInputDevice::allocateInputDevice()); } } XFreeDeviceList(const_cast(devices)); } #endif // EXTENDED_X11_TABLET_SUPPORT } TQt::ButtonState KisCanvasWidget::translateX11ButtonState(int state) { int buttonState = 0; if (state & Button1Mask) buttonState |= Qt::LeftButton; if (state & Button2Mask) buttonState |= Qt::MidButton; if (state & Button3Mask) buttonState |= Qt::RightButton; if (state & ShiftMask) buttonState |= TQt::ShiftButton; if (state & ControlMask) buttonState |= TQt::ControlButton; if (state & X11AltMask) buttonState |= TQt::AltButton; if (state & X11MetaMask) buttonState |= TQt::MetaButton; return static_cast(buttonState); } TQt::ButtonState KisCanvasWidget::translateX11Button(unsigned int X11Button) { TQt::ButtonState qtButton; switch (X11Button) { case Button1: qtButton = Qt::LeftButton; break; case Button2: qtButton = Qt::MidButton; break; case Button3: qtButton = Qt::RightButton; break; default: qtButton = Qt::NoButton; } return qtButton; } #if defined(EXTENDED_X11_TABLET_SUPPORT) KisCanvasWidget::X11TabletDevice::X11TabletDevice() { m_mightBeTabletDevice = false; m_inputDevice = KisInputDevice::unknown(); m_enabled = false; m_xAxis = NoAxis; m_yAxis = NoAxis; m_pressureAxis = NoAxis; m_xTiltAxis = NoAxis; m_yTiltAxis = NoAxis; m_wheelAxis = NoAxis; m_toolIDAxis = NoAxis; m_serialNumberAxis = NoAxis; m_buttonPressEvent = -1; m_buttonReleaseEvent = -1; m_motionNotifyEvent = -1; m_proximityInEvent = -1; m_proximityOutEvent = -1; } KisCanvasWidget::X11TabletDevice::X11TabletDevice(const XDeviceInfo *deviceInfo) { m_mightBeTabletDevice = false; m_inputDevice = KisInputDevice::unknown(); m_enabled = false; m_xAxis = NoAxis; m_yAxis = NoAxis; m_pressureAxis = NoAxis; m_xTiltAxis = NoAxis; m_yTiltAxis = NoAxis; m_wheelAxis = NoAxis; m_toolIDAxis = NoAxis; m_serialNumberAxis = NoAxis; m_deviceId = deviceInfo->id; m_name = deviceInfo->name; // Get the ranges of the valuators XAnyClassPtr classInfo = const_cast(deviceInfo->inputclassinfo); for (int i = 0; i < deviceInfo->num_classes; i++) { if (classInfo->c_class == ValuatorClass) { const XValuatorInfo *valuatorInfo = reinterpret_cast(classInfo); // Need at least x, y, and pressure. if (valuatorInfo->num_axes >= 3) { for (unsigned int axis = 0; axis < valuatorInfo->num_axes; axis++) { m_axisInfo.append(valuatorInfo->axes[axis]); } m_mightBeTabletDevice = true; } } classInfo = reinterpret_cast(reinterpret_cast(classInfo) + classInfo->length); } // Determine the event types it supports. We're only interested in // buttons and motion at the moment. m_buttonPressEvent = -1; m_buttonReleaseEvent = -1; m_motionNotifyEvent = -1; m_proximityInEvent = -1; m_proximityOutEvent = -1; m_XDevice = XOpenDevice(TQApplication::desktop()->x11Display(), m_deviceId); if (m_XDevice != NULL) { for (int i = 0; i < m_XDevice->num_classes; i++) { XEventClass eventClass; if (m_XDevice->classes[i].input_class == ButtonClass) { DeviceButtonPress(m_XDevice, m_buttonPressEvent, eventClass); m_eventClassList.append(eventClass); DeviceButtonRelease(m_XDevice, m_buttonReleaseEvent, eventClass); m_eventClassList.append(eventClass); } else if (m_XDevice->classes[i].input_class == ValuatorClass) { DeviceMotionNotify(m_XDevice, m_motionNotifyEvent, eventClass); m_eventClassList.append(eventClass); } else if (m_XDevice->classes[i].input_class == ProximityClass) { ProximityIn(m_XDevice, m_proximityInEvent, eventClass); m_eventClassList.append(eventClass); ProximityOut(m_XDevice, m_proximityOutEvent, eventClass); m_eventClassList.append(eventClass); } } // Note: We don't XCloseXDevice() since TQt will have already opened // it, and only one XCloseDevice() call closes it for all opens. } if (m_buttonPressEvent == -1 || m_buttonReleaseEvent == -1 || m_motionNotifyEvent == -1) { m_mightBeTabletDevice = false; } } void KisCanvasWidget::X11TabletDevice::setEnabled(bool enabled) { m_enabled = enabled; } bool KisCanvasWidget::X11TabletDevice::enabled() const { return m_enabled; } TQ_INT32 KisCanvasWidget::X11TabletDevice::numAxes() const { return m_axisInfo.count(); } void KisCanvasWidget::X11TabletDevice::setXAxis(TQ_INT32 axis) { m_xAxis = axis; } void KisCanvasWidget::X11TabletDevice::setYAxis(TQ_INT32 axis) { m_yAxis = axis; } void KisCanvasWidget::X11TabletDevice::setPressureAxis(TQ_INT32 axis) { m_pressureAxis = axis; } void KisCanvasWidget::X11TabletDevice::setXTiltAxis(TQ_INT32 axis) { m_xTiltAxis = axis; } void KisCanvasWidget::X11TabletDevice::setYTiltAxis(TQ_INT32 axis) { m_yTiltAxis = axis; } void KisCanvasWidget::X11TabletDevice::setWheelAxis(TQ_INT32 axis) { m_wheelAxis = axis; } void KisCanvasWidget::X11TabletDevice::setToolIDAxis(TQ_INT32 axis) { m_toolIDAxis = axis; } void KisCanvasWidget::X11TabletDevice::setSerialNumberAxis(TQ_INT32 axis) { m_serialNumberAxis = axis; } TQ_INT32 KisCanvasWidget::X11TabletDevice::xAxis() const { return m_xAxis; } TQ_INT32 KisCanvasWidget::X11TabletDevice::yAxis() const { return m_yAxis; } TQ_INT32 KisCanvasWidget::X11TabletDevice::pressureAxis() const { return m_pressureAxis; } TQ_INT32 KisCanvasWidget::X11TabletDevice::xTiltAxis() const { return m_xTiltAxis; } TQ_INT32 KisCanvasWidget::X11TabletDevice::yTiltAxis() const { return m_yTiltAxis; } TQ_INT32 KisCanvasWidget::X11TabletDevice::wheelAxis() const { return m_wheelAxis; } TQ_INT32 KisCanvasWidget::X11TabletDevice::toolIDAxis() const { return m_toolIDAxis; } TQ_INT32 KisCanvasWidget::X11TabletDevice::serialNumberAxis() const { return m_serialNumberAxis; } void KisCanvasWidget::X11TabletDevice::readSettingsFromConfig() { KisConfig cfg; m_enabled = cfg.tabletDeviceEnabled(m_name); m_xAxis = cfg.tabletDeviceAxis(m_name, "XAxis", DefaultAxis); m_yAxis = cfg.tabletDeviceAxis(m_name, "YAxis", DefaultAxis); m_pressureAxis = cfg.tabletDeviceAxis(m_name, "PressureAxis", DefaultAxis); m_xTiltAxis = cfg.tabletDeviceAxis(m_name, "XTiltAxis", DefaultAxis); m_yTiltAxis = cfg.tabletDeviceAxis(m_name, "YTiltAxis", DefaultAxis); m_wheelAxis = cfg.tabletDeviceAxis(m_name, "WheelAxis", DefaultAxis); m_toolIDAxis = cfg.tabletDeviceAxis(m_name, "ToolIDAxis", DefaultAxis); m_serialNumberAxis = cfg.tabletDeviceAxis(m_name, "SerialNumberAxis", DefaultAxis); if (!m_enabled && m_xAxis == DefaultAxis && m_yAxis == DefaultAxis && m_pressureAxis == DefaultAxis && m_xTiltAxis == DefaultAxis && m_yTiltAxis == DefaultAxis && m_wheelAxis == DefaultAxis && m_toolIDAxis == DefaultAxis && m_serialNumberAxis == DefaultAxis) { // This is the first time this device has been seen. Set up default values, assuming // it's a Wacom pad. m_xAxis = 0; m_yAxis = 1; m_pressureAxis = 2; if (m_axisInfo.count() >= 4) { m_xTiltAxis = 3; } else { m_xTiltAxis = NoAxis; } if (m_axisInfo.count() >= 5) { m_yTiltAxis = 4; } else { m_yTiltAxis = NoAxis; } if (m_axisInfo.count() >= 6) { m_wheelAxis = 5; } else { m_wheelAxis = NoAxis; } // Available since driver version 0.7.2. if (m_axisInfo.count() >= 7) { m_toolIDAxis = 6; } else { m_toolIDAxis = NoAxis; } if (m_axisInfo.count() >= 8) { m_serialNumberAxis = 7; } else { m_serialNumberAxis = NoAxis; } } } void KisCanvasWidget::X11TabletDevice::writeSettingsToConfig() { KisConfig cfg; cfg.setTabletDeviceEnabled(m_name, m_enabled); cfg.setTabletDeviceAxis(m_name, "XAxis", m_xAxis); cfg.setTabletDeviceAxis(m_name, "YAxis", m_yAxis); cfg.setTabletDeviceAxis(m_name, "PressureAxis", m_pressureAxis); cfg.setTabletDeviceAxis(m_name, "XTiltAxis", m_xTiltAxis); cfg.setTabletDeviceAxis(m_name, "YTiltAxis", m_yTiltAxis); cfg.setTabletDeviceAxis(m_name, "WheelAxis", m_wheelAxis); cfg.setTabletDeviceAxis(m_name, "ToolIDAxis", m_toolIDAxis); cfg.setTabletDeviceAxis(m_name, "SerialNumberAxis", m_serialNumberAxis); } void KisCanvasWidget::X11TabletDevice::enableEvents(TQWidget *widget) const { if (!m_eventClassList.isEmpty()) { int result = XSelectExtensionEvent(TQT_TQPAINTDEVICE(widget)->x11AppDisplay(), widget->handle(), const_cast(&m_eventClassList[0]), m_eventClassList.count()); if (result != Success) { kdDebug(41001) << "Failed to select extension events for " << m_name << endl; } } } double KisCanvasWidget::X11TabletDevice::translateAxisValue(int value, const XAxisInfo& axisInfo) const { int axisRange = axisInfo.max_value - axisInfo.min_value; double translatedValue = 0; if (axisRange != 0) { translatedValue = (static_cast(value) - axisInfo.min_value) / axisRange; if (axisInfo.min_value < 0) { translatedValue -= 0.5; } } return translatedValue; } KisCanvasWidget::X11TabletDevice::State::State(const KisPoint& pos, double pressure, const KisVector2D& tilt, double wheel, TQ_UINT32 toolID, TQ_UINT32 serialNumber) : m_pos(pos), m_pressure(pressure), m_tilt(tilt), m_wheel(wheel), m_toolID(toolID), m_serialNumber(serialNumber) { } KisCanvasWidget::X11TabletDevice::State KisCanvasWidget::X11TabletDevice::translateAxisData(const int *axisData) const { KisPoint pos(0, 0); if (m_xAxis != NoAxis && m_yAxis != NoAxis) { pos = KisPoint(translateAxisValue(axisData[m_xAxis], m_axisInfo[m_xAxis]), translateAxisValue(axisData[m_yAxis], m_axisInfo[m_yAxis])); } double pressure = PRESSURE_DEFAULT; if (m_pressureAxis != NoAxis) { pressure = translateAxisValue(axisData[m_pressureAxis], m_axisInfo[m_pressureAxis]); } KisVector2D tilt = KisVector2D(0, 0); TQ_UINT32 toolID = 0; TQ_UINT32 serialNumber = 0; if (m_xTiltAxis != NoAxis) { // Latest wacom driver returns the tool id and serial number in // the upper 16 bits of the x and y tilts and wheel. int xTiltAxisValue = (TQ_INT16)(axisData[m_xTiltAxis] & 0xffff); toolID = ((TQ_UINT32)axisData[m_xTiltAxis] >> 16) & 0xffff; tilt.setX(translateAxisValue(xTiltAxisValue, m_axisInfo[m_xTiltAxis])); } if (m_yTiltAxis != NoAxis) { int yTiltAxisValue = (TQ_INT16)(axisData[m_yTiltAxis] & 0xffff); serialNumber = (TQ_UINT32)axisData[m_yTiltAxis] & 0xffff0000; tilt.setY(translateAxisValue(yTiltAxisValue, m_axisInfo[m_yTiltAxis])); } double wheel = 0; if (m_wheelAxis != NoAxis) { int wheelAxisValue = (TQ_INT16)(axisData[m_wheelAxis] & 0xffff); serialNumber |= ((TQ_UINT32)axisData[m_wheelAxis] >> 16) & 0xffff; wheel = translateAxisValue(wheelAxisValue, m_axisInfo[m_wheelAxis]); } //TQString ids; //ids.sprintf("Tool ID: %8x Serial Number: %8x", toolID, serialNumber); return State(pos, pressure, tilt, wheel, toolID, serialNumber); } KisCanvasWidget::X11XIDTabletDeviceMap& KisCanvasWidget::tabletDeviceMap() { return X11TabletDeviceMap; } void KisCanvasWidget::selectTabletDeviceEvents(TQWidget *widget) { for (X11XIDTabletDeviceMap::const_iterator it = X11TabletDeviceMap.begin(); it != X11TabletDeviceMap.end(); ++it) { const X11TabletDevice& device = (*it).second; if (device.enabled()) { device.enableEvents(widget); } } } #endif // EXTENDED_X11_TABLET_SUPPORT bool KisCanvasWidget::x11Event(XEvent *event, Display *x11Display, WId winId, TQPoint widgetOriginPos) { if (event->type == MotionNotify) { // Mouse move if (!m_enableMoveEventCompressionHint) { XMotionEvent motion = event->xmotion; TQPoint globalPos(motion.x_root, motion.y_root); if (globalPos.x() != m_lastRootX || globalPos.y() != m_lastRootY) { int state = translateX11ButtonState(motion.state); TQPoint pos(motion.x, motion.y); TQMouseEvent e(TQEvent::MouseMove, pos, globalPos, Qt::NoButton, state); widgetGotMouseMoveEvent(&e); } m_lastRootX = globalPos.x(); m_lastRootY = globalPos.y(); return true; } else { return false; } } else #if defined(EXTENDED_X11_TABLET_SUPPORT) if (event->type == X11DeviceMotionNotifyEvent || event->type == X11DeviceButtonPressEvent || event->type == X11DeviceButtonReleaseEvent) { // Tablet event. int deviceId; const int *axisData; TQt::ButtonState button; TQt::ButtonState buttonState; if (event->type == X11DeviceMotionNotifyEvent) { // Tablet move const XDeviceMotionEvent *motion = reinterpret_cast(event); XEvent mouseEvent; // Look for an accompanying core event. if (XCheckTypedWindowEvent(x11Display, winId, MotionNotify, &mouseEvent)) { if (motion->time == mouseEvent.xmotion.time) { // Do nothing } else { XPutBackEvent(x11Display, &mouseEvent); } } if (m_enableMoveEventCompressionHint) { while (true) { // Look for another motion notify in the queue and skip // to that if found. if (!XCheckTypedWindowEvent(x11Display, winId, X11DeviceMotionNotifyEvent, &mouseEvent)) { break; } motion = reinterpret_cast(&mouseEvent); XEvent coreMotionEvent; // Look for an accompanying core event. if (!XCheckTypedWindowEvent(x11Display, winId, MotionNotify, &coreMotionEvent)) { // Do nothing } } } deviceId = motion->deviceid; axisData = motion->axis_data; button = Qt::NoButton; buttonState = translateX11ButtonState(motion->state); } else if (event->type == X11DeviceButtonPressEvent) { // Tablet button press const XDeviceButtonPressedEvent *buttonPressed = reinterpret_cast(event); deviceId = buttonPressed->deviceid; axisData = buttonPressed->axis_data; button = translateX11Button(buttonPressed->button); buttonState = translateX11ButtonState(buttonPressed->state); if (TQApplication::activePopupWidget() == 0) { XEvent mouseEvent; // Look for and swallow an accompanying core event, but only if there's // no active popup, as that needs to see it. if (XCheckTypedWindowEvent(x11Display, winId, ButtonPress, &mouseEvent)) { if (buttonPressed->time == mouseEvent.xbutton.time) { // Do nothing } else { XPutBackEvent(x11Display, &mouseEvent); } } } } else { // Tablet button release const XDeviceButtonReleasedEvent *buttonReleased = reinterpret_cast(event); deviceId = buttonReleased->deviceid; axisData = buttonReleased->axis_data; button = translateX11Button(buttonReleased->button); buttonState = translateX11ButtonState(buttonReleased->state); if (TQApplication::activePopupWidget() == 0) { XEvent mouseEvent; // Look for and swallow an accompanying core event, but only if there's // no active popup, as that needs to see it. if (XCheckTypedWindowEvent(x11Display, winId, ButtonRelease, &mouseEvent)) { if (buttonReleased->time == mouseEvent.xbutton.time) { // Do nothing } else { XPutBackEvent(x11Display, &mouseEvent); } } } } X11XIDTabletDeviceMap::const_iterator it = X11TabletDeviceMap.find(deviceId); if (it != X11TabletDeviceMap.end()) { const X11TabletDevice& tabletDevice = (*it).second; if (tabletDevice.enabled()) { X11TabletDevice::State deviceState = tabletDevice.translateAxisData(axisData); // Map normalised position coordinates to screen coordinates TQDesktopWidget *desktop = TQApplication::desktop(); KisPoint globalPos(deviceState.pos().x() * desktop->width(), deviceState.pos().y() * desktop->height()); // Convert screen coordinates to widget coordinates KisPoint pos = globalPos - KoPoint( widgetOriginPos ); // Map tilt to -60 - +60 degrees KisVector2D tilt(deviceState.tilt().x() * 60, deviceState.tilt().y() * 60); if (event->type == X11DeviceMotionNotifyEvent) { KisMoveEvent e(tabletDevice.inputDevice(), pos, globalPos, deviceState.pressure(), tilt.x(), tilt.y(), buttonState); translateTabletEvent(&e); } else if (event->type == X11DeviceButtonPressEvent) { KisButtonPressEvent e(tabletDevice.inputDevice(), pos, globalPos, deviceState.pressure(), tilt.x(), tilt.y(), button, buttonState); translateTabletEvent(&e); } else { KisButtonReleaseEvent e(tabletDevice.inputDevice(), pos, globalPos, deviceState.pressure(), tilt.x(), tilt.y(), button, buttonState); translateTabletEvent(&e); } } // Consume the event even if the device is disabled otherwise TQt will // process it and send a TQTabletEvent. return true; } else { return false; } } else #endif // EXTENDED_X11_TABLET_SUPPORT { return false; } } #if defined(EXTENDED_X11_TABLET_SUPPORT) KisInputDevice KisCanvasWidget::findActiveInputDevice() { X11XIDTabletDeviceMap::const_iterator it; for (it = X11TabletDeviceMap.begin(); it != X11TabletDeviceMap.end(); ++it) { const X11TabletDevice& tabletDevice = (*it).second; XDeviceState *deviceState = XQueryDeviceState(TQApplication::desktop()->x11Display(), tabletDevice.xDevice()); // If your the laptop sleeps, and you remove the mouse from the usb // port, then on wake-up Chalk can crash because the above call will // return 0. if (!deviceState) continue; const XInputClass *inputClass = deviceState->data; bool deviceIsInProximity = false; for (int i = 0; i < deviceState->num_classes; i++) { if (inputClass->c_class == ValuatorClass) { const XValuatorState *valuatorState = reinterpret_cast(inputClass); if ((valuatorState->mode & ProximityState) == InProximity) { deviceIsInProximity = true; break; } } inputClass = reinterpret_cast(reinterpret_cast(inputClass) + inputClass->length); } XFreeDeviceState(deviceState); if (deviceIsInProximity && tabletDevice.enabled()) { return tabletDevice.inputDevice(); } } return KisInputDevice::mouse(); } #endif // EXTENDED_X11_TABLET_SUPPORT #endif // Q_WS_X11 /*************************************************************************/ #define TQPAINTDEVICE_CANVAS_WIDGET false #define OPENGL_CANVAS_WIDGET true KisCanvas::KisCanvas(TQWidget *parent, const char *name) { m_parent = parent; m_name = name; m_enableMoveEventCompressionHint = false; m_canvasWidget = 0; m_useOpenGL = false; createCanvasWidget(TQPAINTDEVICE_CANVAS_WIDGET); } KisCanvas::~KisCanvas() { delete m_canvasWidget; } #ifdef HAVE_GL void KisCanvas::createCanvasWidget(bool useOpenGL, TQGLWidget *sharedContextWidget) #else void KisCanvas::createCanvasWidget(bool useOpenGL) #endif { delete m_canvasWidget; #ifndef HAVE_GL useOpenGL = false; #else if (useOpenGL && !TQGLFormat::hasOpenGL()) { kdDebug(41001) << "Tried to create OpenGL widget when system doesn't have OpenGL\n"; useOpenGL = false; } if (useOpenGL) { m_canvasWidget = new KisOpenGLCanvasWidget(m_parent, m_name.latin1(), sharedContextWidget); } else #endif { m_canvasWidget = new KisTQPaintDeviceCanvasWidget(m_parent, m_name.latin1()); } m_useOpenGL = useOpenGL; TQ_CHECK_PTR(m_canvasWidget); TQWidget *widget = dynamic_cast(m_canvasWidget); widget->setBackgroundMode(TQWidget::NoBackground); widget->setMouseTracking(true); widget->setAcceptDrops(true); m_canvasWidget->enableMoveEventCompressionHint(m_enableMoveEventCompressionHint); #if defined(EXTENDED_X11_TABLET_SUPPORT) selectTabletDeviceEvents(); #endif connect(m_canvasWidget, TQT_SIGNAL(sigGotPaintEvent(TQPaintEvent *)), TQT_SIGNAL(sigGotPaintEvent(TQPaintEvent *))); connect(m_canvasWidget, TQT_SIGNAL(sigGotEnterEvent(TQEvent*)), TQT_SIGNAL(sigGotEnterEvent(TQEvent*))); connect(m_canvasWidget, TQT_SIGNAL(sigGotLeaveEvent(TQEvent*)), TQT_SIGNAL(sigGotLeaveEvent(TQEvent*))); connect(m_canvasWidget, TQT_SIGNAL(sigGotMouseWheelEvent(TQWheelEvent*)), TQT_SIGNAL(sigGotMouseWheelEvent(TQWheelEvent*))); connect(m_canvasWidget, TQT_SIGNAL(sigGotKeyPressEvent(TQKeyEvent*)), TQT_SIGNAL(sigGotKeyPressEvent(TQKeyEvent*))); connect(m_canvasWidget, TQT_SIGNAL(sigGotKeyReleaseEvent(TQKeyEvent*)), TQT_SIGNAL(sigGotKeyReleaseEvent(TQKeyEvent*))); connect(m_canvasWidget, TQT_SIGNAL(sigGotDragEnterEvent(TQDragEnterEvent*)), TQT_SIGNAL(sigGotDragEnterEvent(TQDragEnterEvent*))); connect(m_canvasWidget, TQT_SIGNAL(sigGotDropEvent(TQDropEvent*)), TQT_SIGNAL(sigGotDropEvent(TQDropEvent*))); connect(m_canvasWidget, TQT_SIGNAL(sigGotMoveEvent(KisMoveEvent *)), TQT_SIGNAL(sigGotMoveEvent(KisMoveEvent *))); connect(m_canvasWidget, TQT_SIGNAL(sigGotButtonPressEvent(KisButtonPressEvent *)), TQT_SIGNAL(sigGotButtonPressEvent(KisButtonPressEvent *))); connect(m_canvasWidget, TQT_SIGNAL(sigGotButtonReleaseEvent(KisButtonReleaseEvent *)), TQT_SIGNAL(sigGotButtonReleaseEvent(KisButtonReleaseEvent *))); connect(m_canvasWidget, TQT_SIGNAL(sigGotDoubleClickEvent(KisDoubleClickEvent *)), TQT_SIGNAL(sigGotDoubleClickEvent(KisDoubleClickEvent *))); } void KisCanvas::createTQPaintDeviceCanvas() { createCanvasWidget(TQPAINTDEVICE_CANVAS_WIDGET); } #ifdef HAVE_GL void KisCanvas::createOpenGLCanvas(TQGLWidget *sharedContextWidget) { createCanvasWidget(OPENGL_CANVAS_WIDGET, sharedContextWidget); } #endif bool KisCanvas::isOpenGLCanvas() const { return m_useOpenGL; } void KisCanvas::enableMoveEventCompressionHint(bool enableMoveCompression) { m_enableMoveEventCompressionHint = enableMoveCompression; if (m_canvasWidget != 0) { m_canvasWidget->enableMoveEventCompressionHint(enableMoveCompression); } } TQWidget *KisCanvas::TQPaintDeviceWidget() const { if (m_useOpenGL) { return 0; } else { return dynamic_cast(m_canvasWidget); } } #ifdef HAVE_GL TQGLWidget *KisCanvas::OpenGLWidget() const { if (m_useOpenGL) { return dynamic_cast(m_canvasWidget); } else { return 0; } } #endif KisCanvasWidgetPainter *KisCanvas::createPainter() { Q_ASSERT(m_canvasWidget != 0); return m_canvasWidget->createPainter(); } KisCanvasWidget *KisCanvas::canvasWidget() const { return m_canvasWidget; } void KisCanvas::setGeometry(int x, int y, int width, int height) { Q_ASSERT(m_canvasWidget); dynamic_cast(m_canvasWidget)->setGeometry(x, y, width, height); } void KisCanvas::show() { Q_ASSERT(m_canvasWidget); dynamic_cast(m_canvasWidget)->show(); } void KisCanvas::hide() { Q_ASSERT(m_canvasWidget); dynamic_cast(m_canvasWidget)->hide(); } int KisCanvas::width() const { Q_ASSERT(m_canvasWidget); return dynamic_cast(m_canvasWidget)->width(); } int KisCanvas::height() const { Q_ASSERT(m_canvasWidget); return dynamic_cast(m_canvasWidget)->height(); } void KisCanvas::update() { Q_ASSERT(m_canvasWidget); dynamic_cast(m_canvasWidget)->update(); } void KisCanvas::update(const TQRect& r) { Q_ASSERT(m_canvasWidget); dynamic_cast(m_canvasWidget)->update(r); } void KisCanvas::update(int x, int y, int width, int height) { Q_ASSERT(m_canvasWidget); dynamic_cast(m_canvasWidget)->update(x, y, width, height); } void KisCanvas::repaint() { Q_ASSERT(m_canvasWidget); dynamic_cast(m_canvasWidget)->repaint(); } void KisCanvas::repaint(bool erase) { Q_ASSERT(m_canvasWidget); dynamic_cast(m_canvasWidget)->repaint(erase); } void KisCanvas::repaint(int x, int y, int width, int height, bool erase) { Q_ASSERT(m_canvasWidget); dynamic_cast(m_canvasWidget)->repaint(x, y, width, height, erase); } void KisCanvas::repaint(const TQRect& r, bool erase) { Q_ASSERT(m_canvasWidget); dynamic_cast(m_canvasWidget)->repaint(r, erase); } void KisCanvas::repaint(const TQRegion& r, bool erase) { Q_ASSERT(m_canvasWidget); dynamic_cast(m_canvasWidget)->repaint(r, erase); } bool KisCanvas::isUpdatesEnabled() const { Q_ASSERT(m_canvasWidget); return dynamic_cast(m_canvasWidget)->isUpdatesEnabled(); } void KisCanvas::setUpdatesEnabled(bool updatesEnabled) { Q_ASSERT(m_canvasWidget); dynamic_cast(m_canvasWidget)->setUpdatesEnabled(updatesEnabled); } void KisCanvas::updateGeometry() { Q_ASSERT(m_canvasWidget); dynamic_cast(m_canvasWidget)->updateGeometry(); } void KisCanvas::setFocusPolicy(TQ_FocusPolicy focusPolicy) { Q_ASSERT(m_canvasWidget); dynamic_cast(m_canvasWidget)->setFocusPolicy(focusPolicy); } const TQCursor& KisCanvas::cursor() const { Q_ASSERT(m_canvasWidget); return dynamic_cast(m_canvasWidget)->cursor(); } void KisCanvas::setCursor(const TQCursor& cursor) { Q_ASSERT(m_canvasWidget); dynamic_cast(m_canvasWidget)->setCursor(cursor); } #if defined(EXTENDED_X11_TABLET_SUPPORT) void KisCanvas::selectTabletDeviceEvents() { Q_ASSERT(m_canvasWidget); m_canvasWidget->selectTabletDeviceEvents(); } #endif bool KisCanvas::cursorIsOverCanvas() const { if (TQApplication::activePopupWidget() != 0) { return false; } if (TQApplication::activeModalWidget() != 0) { return false; } TQWidget *canvasWidget = dynamic_cast(m_canvasWidget); Q_ASSERT(canvasWidget != 0); if (canvasWidget) { if (TQApplication::widgetAt(TQCursor::pos(), true) == canvasWidget) { return true; } } return false; } void KisCanvas::handleKeyEvent(TQEvent *e) { TQKeyEvent *ke = dynamic_cast(e); Q_ASSERT(ke != 0); if (ke) { TQWidget *canvasWidget = dynamic_cast(m_canvasWidget); Q_ASSERT(canvasWidget != 0); if (canvasWidget) { canvasWidget->setFocus(); if (e->type() == TQEvent::KeyPress) { emit sigGotKeyPressEvent(ke); } else { emit sigGotKeyReleaseEvent(ke); } } } } #include "kis_canvas.moc"