[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