diff options
Diffstat (limited to 'chalk/ui/kis_canvas.cpp')
-rw-r--r-- | chalk/ui/kis_canvas.cpp | 1356 |
1 files changed, 1356 insertions, 0 deletions
diff --git a/chalk/ui/kis_canvas.cpp b/chalk/ui/kis_canvas.cpp new file mode 100644 index 00000000..63ae4ab0 --- /dev/null +++ b/chalk/ui/kis_canvas.cpp @@ -0,0 +1,1356 @@ +/* + * Copyright (c) 1999 Matthias Elter <me@kde.org> + * Copyright (c) 2004-2006 Adrian Page <adrian@pagenet.plus.com> + * + * 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 <tqcursor.h> + +#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 <tqdesktopwidget.h> +#include <tqapplication.h> + +#include <X11/XKBlib.h> +#include <X11/keysym.h> + +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<XID, KisCanvasWidget::X11TabletDevice> 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<TQWidget *>(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<KisButtonEvent *>(e); + + if (b->button() == Qt::MidButton || b->button() == Qt::RightButton) { + + if (e->type() == KisEvent::ButtonPressEvent) { + buttonPressEvent(static_cast<KisButtonPressEvent *>(e)); + } else { + buttonReleaseEvent(static_cast<KisButtonReleaseEvent *>(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<XDeviceInfo *>(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<TQt::ButtonState>(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<XAnyClassPtr>(deviceInfo->inputclassinfo); + + for (int i = 0; i < deviceInfo->num_classes; i++) { + + if (classInfo->c_class == ValuatorClass) { + + const XValuatorInfo *valuatorInfo = reinterpret_cast<const XValuatorInfo *>(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<XAnyClassPtr>(reinterpret_cast<char *>(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<XEventClass*>(&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<double>(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<const XDeviceMotionEvent *>(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<const XDeviceMotionEvent *>(&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<const XDeviceButtonPressedEvent *>(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<const XDeviceButtonReleasedEvent *>(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<const XValuatorState *>(inputClass); + + if ((valuatorState->mode & ProximityState) == InProximity) { + deviceIsInProximity = true; + break; + } + } + + inputClass = reinterpret_cast<const XInputClass *>(reinterpret_cast<const char *>(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<TQWidget *>(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<TQWidget *>(m_canvasWidget); + } +} + +#ifdef HAVE_GL +TQGLWidget *KisCanvas::OpenGLWidget() const +{ + if (m_useOpenGL) { + return dynamic_cast<TQGLWidget *>(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<TQWidget *>(m_canvasWidget)->setGeometry(x, y, width, height); +} + +void KisCanvas::show() +{ + Q_ASSERT(m_canvasWidget); + dynamic_cast<TQWidget *>(m_canvasWidget)->show(); +} + +void KisCanvas::hide() +{ + Q_ASSERT(m_canvasWidget); + dynamic_cast<TQWidget *>(m_canvasWidget)->hide(); +} + +int KisCanvas::width() const +{ + Q_ASSERT(m_canvasWidget); + return dynamic_cast<TQWidget *>(m_canvasWidget)->width(); +} + +int KisCanvas::height() const +{ + Q_ASSERT(m_canvasWidget); + return dynamic_cast<TQWidget *>(m_canvasWidget)->height(); +} + +void KisCanvas::update() +{ + Q_ASSERT(m_canvasWidget); + dynamic_cast<TQWidget *>(m_canvasWidget)->update(); +} + +void KisCanvas::update(const TQRect& r) +{ + Q_ASSERT(m_canvasWidget); + dynamic_cast<TQWidget *>(m_canvasWidget)->update(r); +} + +void KisCanvas::update(int x, int y, int width, int height) +{ + Q_ASSERT(m_canvasWidget); + dynamic_cast<TQWidget *>(m_canvasWidget)->update(x, y, width, height); +} + +void KisCanvas::repaint() +{ + Q_ASSERT(m_canvasWidget); + dynamic_cast<TQWidget *>(m_canvasWidget)->repaint(); +} + +void KisCanvas::repaint(bool erase) +{ + Q_ASSERT(m_canvasWidget); + dynamic_cast<TQWidget *>(m_canvasWidget)->repaint(erase); +} + +void KisCanvas::repaint(int x, int y, int width, int height, bool erase) +{ + Q_ASSERT(m_canvasWidget); + dynamic_cast<TQWidget *>(m_canvasWidget)->repaint(x, y, width, height, erase); +} + +void KisCanvas::repaint(const TQRect& r, bool erase) +{ + Q_ASSERT(m_canvasWidget); + dynamic_cast<TQWidget *>(m_canvasWidget)->repaint(r, erase); +} + +void KisCanvas::repaint(const TQRegion& r, bool erase) +{ + Q_ASSERT(m_canvasWidget); + dynamic_cast<TQWidget *>(m_canvasWidget)->repaint(r, erase); +} + +bool KisCanvas::isUpdatesEnabled() const +{ + Q_ASSERT(m_canvasWidget); + return dynamic_cast<TQWidget *>(m_canvasWidget)->isUpdatesEnabled(); +} + +void KisCanvas::setUpdatesEnabled(bool updatesEnabled) +{ + Q_ASSERT(m_canvasWidget); + dynamic_cast<TQWidget *>(m_canvasWidget)->setUpdatesEnabled(updatesEnabled); +} + +void KisCanvas::updateGeometry() +{ + Q_ASSERT(m_canvasWidget); + dynamic_cast<TQWidget *>(m_canvasWidget)->updateGeometry(); +} + +void KisCanvas::setFocusPolicy(TQ_FocusPolicy focusPolicy) +{ + Q_ASSERT(m_canvasWidget); + dynamic_cast<TQWidget *>(m_canvasWidget)->setFocusPolicy(focusPolicy); +} + +const TQCursor& KisCanvas::cursor() const +{ + Q_ASSERT(m_canvasWidget); + return dynamic_cast<TQWidget *>(m_canvasWidget)->cursor(); +} + +void KisCanvas::setCursor(const TQCursor& cursor) +{ + Q_ASSERT(m_canvasWidget); + dynamic_cast<TQWidget *>(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<TQWidget *>(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<TQKeyEvent *>(e); + + Q_ASSERT(ke != 0); + + if (ke) { + TQWidget *canvasWidget = dynamic_cast<TQWidget *>(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" + |