[rkward-cvs] SF.net SVN: rkward-code:[4714] trunk/rkward
tfry at users.sf.net
tfry at users.sf.net
Tue Apr 23 17:59:00 UTC 2013
Revision: 4714
http://sourceforge.net/p/rkward/code/4714
Author: tfry
Date: 2013-04-23 17:58:59 +0000 (Tue, 23 Apr 2013)
Log Message:
-----------
Implement support for getGraphicsEvent() in the RK() device. Some cleanup still needed.
Modified Paths:
--------------
trunk/rkward/ChangeLog
trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice.cpp
trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice.h
trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice_frontendtransmitter.cpp
trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice_protocol_shared.h
trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice_setup.cpp
trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice_stubs.cpp
Modified: trunk/rkward/ChangeLog
===================================================================
--- trunk/rkward/ChangeLog 2013-04-23 10:48:18 UTC (rev 4713)
+++ trunk/rkward/ChangeLog 2013-04-23 17:58:59 UTC (rev 4714)
@@ -4,7 +4,8 @@
- Shortcuts for the "Run ..." actions have been changed for better cross-platform compatibility
- The script editor's "Run line" and "Run selection" actions have been merged
- New (experimental) RKWard native on-screen device (RK())
- TODO: Finish it, then provide UI option to select on-screen device
+ TODO: Finish it, then provide UI option to select on-screen device
+ - take care of the warnings in rkgraphicsdevice_stubs.cpp
- New R function rk.without.plot.history() for turning off plot history, temporarily
- Add command line option --backend-debugger
Modified: trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice.cpp 2013-04-23 10:48:18 UTC (rev 4713)
+++ trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice.cpp 2013-04-23 17:58:59 UTC (rev 4714)
@@ -45,6 +45,7 @@
view = new QLabel ();
view->installEventFilter (this);
view->setScaledContents (true); // this is just for preview during scaling. The area will be re-sized and re-drawn from R.
+ view->setFocusPolicy (Qt::StrongFocus); // for receiving key events for R's getGraphicsEvent()
connect (view, SIGNAL (destroyed(QObject*)), this, SLOT (viewKilled()));
connect (&updatetimer, SIGNAL (timeout ()), this, SLOT (updateNow ()));
updatetimer.setSingleShot (true);
@@ -270,6 +271,37 @@
stopInteraction ();
}
+void RKGraphicsDevice::startGettingEvents (const QString& prompt) {
+ RK_TRACE (GRAPHICS_DEVICE);
+
+ RK_ASSERT (interaction_opcode < 0);
+ stored_events.clear ();
+ interaction_opcode = RKDStartGettingEvents;
+
+ view->setCursor (Qt::CrossCursor);
+ view->setToolTip (prompt);
+ view->show ();
+ view->raise ();
+}
+
+RKGraphicsDevice::StoredEvent RKGraphicsDevice::fetchNextEvent () {
+ RK_TRACE (GRAPHICS_DEVICE);
+
+ if (stored_events.isEmpty ()) {
+ StoredEvent ret;
+ ret.event_code = RKDNothing;
+ return ret;
+ }
+ return stored_events.takeFirst ();
+}
+
+void RKGraphicsDevice::stopGettingEvents () {
+ RK_TRACE (GRAPHICS_DEVICE);
+
+ RK_ASSERT (interaction_opcode == RKDStartGettingEvents);
+ stopInteraction ();
+}
+
bool RKGraphicsDevice::eventFilter (QObject *watched, QEvent *event) {
RK_ASSERT (watched == view);
@@ -283,7 +315,41 @@
stopInteraction ();
return true;
}
+ } else if (interaction_opcode == RKDStartGettingEvents) {
+ if ((event->type () == QEvent::MouseButtonPress) || (event->type () == QEvent::MouseButtonRelease) || (event->type () == QEvent::MouseMove)) {
+ QMouseEvent *me = static_cast<QMouseEvent*> (event);
+ StoredEvent sev;
+
+ sev.event_code = event->type () == QEvent::MouseButtonPress ? RKDMouseDown : (event->type () == QEvent::MouseButtonRelease ? RKDMouseUp : RKDMouseMove);
+ sev.x = me->x ();
+ sev.y = me->y ();
+ sev.buttons = 0;
+ if (me->buttons () & Qt::LeftButton) sev.buttons |= RKDMouseLeftButton;
+ if (me->buttons () & Qt::MiddleButton) sev.buttons |= RKDMouseMiddleButton;
+ if (me->buttons () & Qt::RightButton) sev.buttons |= RKDMouseRightButton;
+
+ stored_events.append (sev);
+ return (true);
+ } else if (event->type () == QEvent::KeyPress) {
+ QKeyEvent *ke = static_cast<QKeyEvent*> (event);
+ StoredEvent sev;
+
+ sev.event_code = RKDKeyPress;
+ sev.keytext = ke->text ();
+ sev.keycode = ke->key ();
+ sev.modifiers = ke->modifiers ();
+ if (sev.modifiers - (sev.modifiers & Qt::ShiftModifier)) {
+ // well, the text returned in ke->text() is a bit strange, sometimes when modifiers are involved.
+ // This HACK does some sanitizing. Too much sanitizing? Umlauts don't get through this, for one thing.
+ sev.keytext = QKeySequence (sev.modifiers | sev.keycode).toString ();
+ sev.keytext = sev.keytext.right (1);
+ }
+
+ stored_events.append (sev);
+ return (true);
+ }
}
+
if (event->type () == QEvent::Resize) triggerUpdate ();
return false;
@@ -297,6 +363,12 @@
} else if (interaction_opcode == RKDNewPageConfirm) {
RK_ASSERT (dialog);
emit (newPageConfirmDone (true));
+ } else if (interaction_opcode == RKDStartGettingEvents) {
+ // not much to do, fortunately, as getting graphics events is non-blocking
+ stored_events.clear ();
+ StoredEvent sev;
+ sev.event_code = RKDFrontendCancel;
+ stored_events.append (sev); // In case the backend keeps asking (without calling RKDStartGettingEvents again, first), we'll tell it to stop.
}
if (dialog) {
Modified: trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice.h
===================================================================
--- trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice.h 2013-04-23 10:48:18 UTC (rev 4713)
+++ trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice.h 2013-04-23 17:58:59 UTC (rev 4714)
@@ -55,6 +55,21 @@
void locator ();
void confirmNewPage ();
+ // graphics event handling
+/** Simple struct to keep info about both mouse and keyboard events, so we can store them in a list, until R fetches them. */
+ struct StoredEvent {
+ StoredEvent () : event_code (0), buttons (0), modifiers (0), keycode (0), x (0), y (0) {};
+ qint8 event_code;
+ qint8 buttons;
+ qint32 modifiers;
+ qint32 keycode;
+ QString keytext;
+ double x, y;
+ };
+ void startGettingEvents (const QString &prompt);
+ StoredEvent fetchNextEvent ();
+ void stopGettingEvents ();
+
QWidget* viewPort () const { return view; };
QSizeF currentSize () const { return view->size (); }
void setAreaSize (const QSize &size);
@@ -80,6 +95,8 @@
KDialog *dialog;
int interaction_opcode; /**< Current interactive operation (from RKDOpcodes enum), or -1 is there is no current interactive operation */
+
+ QList<StoredEvent> stored_events;
};
#endif
Modified: trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice_frontendtransmitter.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice_frontendtransmitter.cpp 2013-04-23 10:48:18 UTC (rev 4713)
+++ trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice_frontendtransmitter.cpp 2013-04-23 17:58:59 UTC (rev 4714)
@@ -297,7 +297,23 @@
device->setAreaSize (size);
} else if (opcode == RKDLocator) {
device->locator ();
-#warning TODO keep track of status
+ } else if (opcode == RKDStartGettingEvents) {
+ QString prompt;
+ streamer.instream >> prompt;
+ device->startGettingEvents (prompt);
+ } else if (opcode == RKDStopGettingEvents) {
+ device->stopGettingEvents ();
+ } else if (opcode == RKDFetchNextEvent) {
+ RKGraphicsDevice::StoredEvent ev = device->fetchNextEvent ();
+ streamer.outstream << (qint8) ev.event_code;
+ if (ev.event_code == RKDKeyPress) {
+ streamer.outstream << ev.keytext << (qint32) ev.keycode << (qint32) ev.modifiers;
+ } else if ((ev.event_code == RKDMouseDown) || (ev.event_code == RKDMouseUp) || (ev.event_code == RKDMouseMove)) {
+ streamer.outstream << (qint8) ev.buttons << (double) ev.x << (double) ev.y;
+ } else {
+ RK_ASSERT ((ev.event_code == RKDNothing) || (ev.event_code == RKDFrontendCancel));
+ }
+ streamer.writeOutBuffer ();
} else if (opcode == RKDNewPageConfirm) {
device->confirmNewPage ();
#warning TODO keep track of status
@@ -331,6 +347,8 @@
streamer.outstream << width;
} else if (opcode == RKDGetSize) {
streamer.outstream << QSizeF ();
+ } else if (opcode == RKDFetchNextEvent) {
+ streamer.outstream << (qint8) RKDNothing;
} else {
return; // nothing to write
}
Modified: trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice_protocol_shared.h
===================================================================
--- trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice_protocol_shared.h 2013-04-23 10:48:18 UTC (rev 4713)
+++ trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice_protocol_shared.h 2013-04-23 17:58:59 UTC (rev 4714)
@@ -62,35 +62,54 @@
enum RKDOpcodes {
// Asynchronous operations
- RKDCreate, // 0
+ RKDCreate, // 0
RKDCircle,
RKDLine,
RKDPolygon,
RKDPolyline,
- RKDRect, // 5
+ RKDRect, // 5
RKDTextUTF8,
RKDNewPage,
RKDClose,
RKDActivate,
- RKDDeActivate, // 10
+ RKDDeActivate, // 10
RKDClip,
RKDMode,
RKDRaster,
RKDSetSize,
+ RKDStartGettingEvents, // 15
+ RKDStopGettingEvents,
// Synchronous operations
- RKDStrWidthUTF8, // 15
+ RKDFetchNextEvent,
+ RKDStrWidthUTF8,
RKDMetricInfo,
- RKDLocator,
+ RKDLocator, // 20
RKDNewPageConfirm,
RKDCapture,
- RKDQueryResolution, // 20
- RKDGetSize,
+ RKDQueryResolution,
+ RKDGetSize,
// Protocol operations
- RKDCancel
+ RKDCancel // 25
};
+enum RKDEventCodes {
+ RKDMouseUp = 0,
+ RKDMouseDown = 1,
+ RKDMouseMove = 2,
+ RKDKeyPress = 3,
+ RKDNothing = 4,
+ RKDFrontendCancel = 5,
+
+// Mouse buttons, or-able, identical to the corresponding R defines. Note: x1 and x2 buttons are not handled by R
+ RKDMouseLeftButton = 1,
+ RKDMouseMiddleButton = 2,
+ RKDMouseRightButton = 4
+// RKDMouseX1Button = 8,
+// RKDMouseX2Button = 16
+};
+
#include <QtGlobal>
typedef quint32 RKGraphicsDeviceTransmittionLengthType;
Modified: trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice_setup.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice_setup.cpp 2013-04-23 10:48:18 UTC (rev 4713)
+++ trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice_setup.cpp 2013-04-23 17:58:59 UTC (rev 4714)
@@ -168,14 +168,15 @@
/*
* Mouse events
*/
-// dev->canGenMouseDown = TRUE;
-// dev->canGenMouseMove = TRUE;
-// dev->canGenMouseUp = TRUE;
-// dev->canGenKeybd = TRUE;
+ dev->canGenMouseDown = TRUE;
+ dev->canGenMouseMove = TRUE;
+ dev->canGenMouseUp = TRUE;
+ dev->canGenKeybd = TRUE;
// gettingEvent; This is set while getGraphicsEvent is actively
// looking for events
-// dev->eventHelper = ...
+ dev->eventHelper = RKD_EventHelper;
+ dev->onExit = RKD_onExit;
#endif
/*
Modified: trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice_stubs.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice_stubs.cpp 2013-04-23 10:48:18 UTC (rev 4713)
+++ trunk/rkward/rkward/rbackend/rkwarddevice/rkgraphicsdevice_stubs.cpp 2013-04-23 17:58:59 UTC (rev 4714)
@@ -23,6 +23,7 @@
#include "rkgraphicsdevice_backendtransmitter.h"
#include "../rkrbackend.h"
#include "../rkreventloop.h"
+#include "../../debug.h"
extern "C" {
#include <R_ext/GraphicsEngine.h>
@@ -64,6 +65,10 @@
checkHandleError ();
}
}
+ if (R_interrupts_pending && have_lock) {
+ RKGraphicsDeviceBackendTransmitter::mutex.unlock ();
+ have_lock = false;
+ }
} END_SUSPEND_INTERRUPTS;
}
@@ -430,3 +435,82 @@
// Return value FALSE: Let R ask, instead
}
+#if R_VERSION >= R_Version (2, 12, 0)
+void RKD_EventHelper (pDevDesc dev, int code) {
+ {
+ RKGraphicsDataStreamWriteGuard wguard;
+ if (code == 1) {
+ QString prompt;
+ if (Rf_isEnvironment (dev->eventEnv)) {
+ SEXP sprompt = Rf_findVar (Rf_install ("prompt"), dev->eventEnv);
+ if (Rf_length (sprompt) == 1) prompt = QString::fromUtf8 (CHAR (Rf_asChar (sprompt)));
+ }
+ WRITE_HEADER (RKDStartGettingEvents, dev);
+ RKD_OUT_STREAM << prompt;
+ return;
+ } else if (code == 0) {
+ WRITE_HEADER (RKDStopGettingEvents, dev);
+ return;
+ } else {
+ WRITE_HEADER (RKDFetchNextEvent, dev);
+ }
+ }
+ RK_ASSERT (code == 2);
+ {
+ RKGraphicsDataStreamReadGuard rguard;
+#warning TODO: read while guard is active
+ }
+ qint8 event_code;
+ RKD_IN_STREAM >> event_code;
+ if (event_code == RKDFrontendCancel) {
+ Rf_error ("Interrupted by user");
+ return; // not reached
+ }
+ if (event_code == RKDNothing) return;
+ else if (event_code == RKDKeyPress) {
+ QString text;
+ qint32 keycode, modifiers;
+ RKD_IN_STREAM >> text >> keycode >> modifiers;
+
+ if (modifiers - (modifiers & Qt::ShiftModifier)) { // any other modifier than Shift. only. NOTE: devX11.c and devWindows.c handle Ctrl, only as of R 3.0.0
+ QString mod_text;
+ if (modifiers & Qt::ControlModifier) mod_text.append ("ctrl-");
+ if (modifiers & Qt::AltModifier) mod_text.append ("alt-");
+ if (modifiers & Qt::MetaModifier) mod_text.append ("meta-");
+ if (text.isEmpty () && (modifiers & Qt::ShiftModifier)) mod_text.append ("shift-"); // don't apply shift text (where it has already been handled)
+ text = mod_text + text.toUpper ();
+ }
+
+ R_KeyName r_key_name = knUNKNOWN;
+ if (keycode == Qt::Key_Left) r_key_name = knLEFT;
+ else if (keycode == Qt::Key_Right) r_key_name = knRIGHT;
+ else if (keycode == Qt::Key_Up) r_key_name = knUP;
+ else if (keycode == Qt::Key_Down) r_key_name = knDOWN;
+ else if ((keycode >= Qt::Key_F1) && (keycode <= Qt::Key_F12)) r_key_name = (R_KeyName) (knF1 + (keycode - Qt::Key_F1));
+ else if (keycode == Qt::Key_PageUp) r_key_name = knPGUP;
+ else if (keycode == Qt::Key_PageDown) r_key_name = knPGDN;
+ else if (keycode == Qt::Key_End) r_key_name = knEND;
+ else if (keycode == Qt::Key_Home) r_key_name = knHOME;
+ else if (keycode == Qt::Key_Insert) r_key_name = knINS;
+ else if (keycode == Qt::Key_Delete) r_key_name = knDEL;
+#warning: TODO: can this result in an error / interrupt?
+ Rf_doKeybd (dev, r_key_name, text.toUtf8 ());
+ } else { // all others are mouse events
+ double x, y;
+ qint8 buttons;
+ RKD_IN_STREAM >> buttons >> x >> y;
+#warning: TODO: can this result in an error / interrupt?
+ Rf_doMouseEvent (dev, event_code == RKDMouseDown ? meMouseDown : (event_code == RKDMouseUp ? meMouseUp : meMouseMove), buttons, x, y);
+ }
+qDebug ("got something");
+}
+
+void RKD_onExit (pDevDesc dev) {
+ if (dev->gettingEvent) {
+ RKGraphicsDataStreamWriteGuard wguard;
+ WRITE_HEADER (RKDStopGettingEvents, dev);
+ }
+ dev->gettingEvent = (Rboolean) false;
+}
+
+#endif
More information about the rkward-tracker
mailing list