The interactions between applications, users and the Photon server are represented by data structures called events.
This chapter discusses:
Event information is stored in structures of type PhEvent_t; see the Photon Library Reference.
Most of the time, you can use a widget's callbacks to handle what the user does while pointing to it. If you're working with event handlers, you'll need to know what events Photon emits.
When you press the pointer button, Photon emits a Ph_EV_BUT_PRESS event to the widget that currently has focus.
When you release the button, Photon emits two Ph_EV_BUT_RELEASE events:
The real release hits whatever the mouse points to when you release the button. The phantom release always goes to the same region (and position) that received the press.
In other words, if your widget saw the press, it also sees the phantom release. And depending on where the mouse was pointing to, you may or may not get the real release. If your widget gets both the real and phantom releases, the real one always comes first.
Whenever you press or release the mouse button, the event includes the click count. How can your application determine that you clicked, instead of double clicked?
There's a click counter in the event data that's associated with Ph_EV_BUT_PRESS and Ph_EV_BUT_RELEASE events; to get this data, call PhGetData(). The data for these events is a structure of type PhPointerEvent_t (see the Photon Library Reference for details); its click_count member gives the number of times that you clicked the mouse button.
If you keep clicking quickly enough without moving the mouse, the counter keeps incrementing. If you move the mouse or stop clicking for a while, the counter resets and Photon emits a Ph_EV_BUT_RELEASE event with a subtype of Ph_EV_RELEASE_ENDCLICK.
In other words, the first click generates a Ph_EV_BUT_PRESS event and a pair of Ph_EV_BUT_RELEASE events (one REAL and one PHANTOM) with click_count set to 1. Then, depending on whether the user clicks again soon enough or not, you get either:
Or:
After the second click, you either get a third one or an ENDCLICK, and so on. But eventually you get an ENDCLICK — and the next time the person clicks, the click count is 1 again.
If you need to determine what keys were pressed in a pointer event, call PhGetData() to get the event data that's included for Ph_EV_BUT_PRESS and Ph_EV_BUT_RELEASE events. The data for these events is a structure of type PhPointerEvent_t (described in the Photon Library Reference); check its key_mods member to determine the modifier keys that were pressed.
For example, this Pt_CB_ACTIVATE callback lists the modifier keys that were pressed when the pointer button was released:
/* Standard headers */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> /* Toolkit headers */ #include <Ph.h> #include <Pt.h> #include <Ap.h> /* Local headers */ #include "abimport.h" #include "proto.h" int check_keys( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo ) { PhPointerEvent_t *event_data; /* eliminate 'unreferenced' warnings */ widget = widget, apinfo = apinfo, cbinfo = cbinfo; if (cbinfo->event->type != Ph_EV_BUT_RELEASE) { printf ("Not a Ph_EV_BUT_RELEASE event\n"); } else { printf ("It's a Ph_EV_BUT_RELEASE event\n"); event_data = (PhPointerEvent_t *) PhGetData (cbinfo->event); if (event_data->key_mods & Pk_KM_Shift ) printf (" Shift\n"); if (event_data->key_mods & Pk_KM_Ctrl ) printf (" Ctrl\n"); if (event_data->key_mods & Pk_KM_Alt ) printf (" Alt\n"); } return( Pt_CONTINUE ); }
This section describes how to use PhEmit(), as well as:
The most general way for your application to emit an event is to call PhEmit():
int PhEmit( PhEvent_t *event, PhRect_t *rects, void *data );
The arguments are:
If you set collector.rid to a region ID, only that region notices the event.
The Photon Manager sets the following members of the event structure after it has enqueued a copy of the event to an application:
If the event-specific data isn't in contiguous memory, you may find PhEmitmx() more useful than PhEmit():
int PhEmitmx( PhEvent_t *event, PhRect_t *rects, int mxparts, struct _mxfer_entry *mx );
The return codes for PhEmit() and PhEmitmx() are:
Sometimes an application needs to target an event directly at a specific region, without making the event travel through the event space before arriving at that region. You can use an inclusive event or a direct event to ensure that the targeted region sees the event.
For an inclusive event, do the following:
If you don't want an inclusive targeted event to continue through the event space, you must make the emitting region opaque to that type of event, or use a direct event instead.
For a direct event, do the following:
If you want to send an event to a specific widget, you could call PhEmit() as described above, but you'll need to look after a lot of details, including making sure:
It's easier to call PtSendEventToWidget(). This function gives the event to the specified widget directly and without delay. It's much more deterministic and efficient than PhEmit().
The prototype is:
int PtSendEventToWidget( PtWidget_t *widget, PhEvent_t *event );
Sometimes you might need to simulate a key press in your application. Depending on what exactly you want to achieve, you can choose from several ways of generating key events:
event->emitter.rid = Ph_DEV_RID;
The rectangle set should consist of a single pixel — if you're not using the window manager, or if PWM is set to use cursor focus, the position of that pixel determines which window the event will hit.
event->collector.rid = rid; event->flags |= Ph_EVENT_DIRECT;
In both of these cases, use a PhKeyEvent_t structure as the event data. For more information, see the Photon Library Reference.
Here's an example:
static void send_key( long key ) { struct{ PhEvent_t event; PhRect_t rect; PhKeyEvent_t pevent; } new_event; PhEvent_t event; PhKeyEvent_t key_event; PhRect_t rect; rect.ul.x = rect.ul.y = 0; rect.lr.x = rect.lr.y = 0; memset( &event , 0, sizeof(event) ); memset( &key_event, 0, sizeof(key_event) ); event.type = Ph_EV_KEY; event.emitter.rid = Ph_DEV_RID; event.num_rects = 1; event.data_len = sizeof(key_event); event.input_group = 1; key_event.key_cap = key; key_event.key_sym = key; if ( isascii( key ) && isupper( key ) ) { key_event.key_mods = Pk_KM_Shift; } /* Emit the key press. */ key_event.key_flags = Pk_KF_Sym_Valid | Pk_KF_Cap_Valid | Pk_KF_Key_Down; PhEmit( &event, &rect, &key_event ); /* Emit the key release. */ key_event.key_flags &= ~(Pk_KF_Key_Down | Pk_KF_Sym_Valid) ; PhEmit( &event ,&rect, &key_event ); return; }
When an event is emitted, the coordinates of its rectangle set are relative to the emitting region's origin. But when the event is collected, its coordinates become relative to the collecting region's origin.
The Photon Manager ensures this happens by translating coordinates accordingly. The translation member of the PhEvent_t specifies the translation between the emitting region's origin and the collecting region's origin.
The PtWidget widget class provides these callbacks for processing events:
These callbacks are called every time a Photon event that matches an event mask (provided by the application) is received. Since all the widget classes in the Photon widget library are descended from the PtWidget, these callbacks can be used with any widget from the Photon widget library.
When you attach a raw or filter callback to a widget, the widget library
creates a region, if necessary, that will pick up specific
events for the widget. This increases the number
of regions the Photon Manager must manage and, as a result,
may reduce performance.
For this reason, use event handlers only when you have to do something that can't be done using standard widget callbacks. If you do use event handlers, consider using them only on window widgets, which already have regions. |
Whenever a Photon event is received, it's passed down the widget family hierarchy until a widget consumes it. (When a widget has processed an event and prevents another widget from interacting with the event, the first widget is said to have consumed the event.)
In general, the Pt_CB_FILTER callbacks are invoked on the way down the hierarchy, and the Pt_CB_RAW callbacks are invoked on the way back up. Each widget processes the event like this:
The value returned by a widget's Pt_CB_RAW callback indicates what's to be done with the event:
If a widget is disabled (i.e. Pt_BLOCKED is set in its Pt_ARG_FLAGS), the raw and filter callbacks aren't invoked. Instead, the widget's Pt_CB_BLOCKED callbacks (if any) are invoked. |
Let's look at a simple widget family to see how this works. Let's suppose you have a window that contains a pane that contains a button. Here's what normally happens when you click on the button:
If the pane's Pt_CB_FILTER callback says to ignore the event:
For information on adding event handlers, see:
Most applications collect events by calling PtMainLoop(). This routine processes Photon events and supports work procedures and input handling.
If your application doesn't use widgets, you can collect events:
However, writing your own mainloop function isn't a trivial matter; it's easier to create at least a disjoint widget (such as PtRegion or PtWindow) and then use PtMainLoop().
PhGetRects() extracts the rectangle set, and PhGetData() extracts the event's data portion.
A region can collect a given event only if portions of the region intersect the event, and the region is sensitive to that type of event. |
The Photon Manager compresses drag, boundary, and pointer events. That is, if an event of that type is pending when another event arrives, the new event will be merged with the unprocessed events. As a result, an application sees only the latest values for these events and is saved from collecting too many unnecessary events.
If you need to capture mouse coordinates, for example to drag graphical objects in your application, you'll need to work with events.
If you want to transfer arbitrary data within your application or between applications, see the Drag and Drop chapter. |
There are two types of dragging:
Dragging is done in two steps:
These steps are discussed in the sections that follow.
Where you initiate the dragging depends on how the user is meant to drag widgets. For example, if the user holds down the left mouse button on a widget to drag it, initiate dragging in the widget's Arm (Pt_CB_ARM) or Outbound (Pt_CB_OUTBOUND) callback. Make sure that Pt_SELECTABLE is set in the widget's Pt_ARG_FLAGS resource.
Dragging is started by calling the PhInitDrag() function:
int PhInitDrag( PhRid_t rid, unsigned flags, PhRect_t *rect, PhRect_t *boundary, unsigned int input_group, PhDim_t *min, PhDim_t *max, const PhDim_t *step, const PhPoint_t *ptrpos, const PhCursorDescription_t *cursor );
The arguments are used as follows:
If Ph_DRAG_TRACK is included in flags, then opaque dragging is used; if Ph_DRAG_TRACK isn't included, outline dragging is used.
The following flags indicate which edge(s) of the dragging rectangle track the pointer:
The following example shows an Arm (Pt_CB_ARM) callback that initiates outline dragging:
/* Start dragging a widget */ /* Standard headers */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> /* Toolkit headers */ #include <Ph.h> #include <Pt.h> #include <Ap.h> /* Local headers */ #include "globals.h" #include "abimport.h" #include "proto.h" int start_dragging( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo ) { PhDim_t *dimension; PhRect_t rect; PhRect_t boundary; /* eliminate 'unreferenced' warnings */ widget = widget, apinfo = apinfo, cbinfo = cbinfo; /* Set the dragging rectangle to the position and size of the widget being dragged. */ PtWidgetExtent (widget, &rect); /* Set the boundary for dragging to the boundary of the window. */ PtGetResource (ABW_base, Pt_ARG_DIM, &dimension, 0); boundary.ul.x = 0; boundary.ul.y = 0; boundary.lr.x = dimension->w - 1; boundary.lr.y = dimension->h - 1; /* Initiate outline dragging (Ph_DRAG_TRACK isn't specified). */ PhInitDrag (PtWidgetRid (ABW_base), Ph_TRACK_DRAG, &rect, &boundary, PhInputGroup( cbinfo->event ), NULL, NULL, NULL, NULL, NULL ); /* Save a pointer to the widget being dragged. */ dragged_widget = widget; return( Pt_CONTINUE ); }
The above callback is added to the Arm (Pt_CB_ARM) callback of the widget to be dragged. It can be used for dragging any widget, so a pointer to the widget is saved in the global variable dragged_widget.
If you want to use opaque dragging, add the Ph_DRAG_TRACK flag to the call to PhInitDrag():
PhInitDrag( PtWidgetRid (ABW_base), Ph_TRACK_DRAG | Ph_DRAG_TRACK, &rect, &boundary, PhInputGroup( cbinfo->event ), NULL, NULL, NULL, NULL, NULL );
To handle drag (Ph_EV_DRAG) events, you need to define a Raw (Pt_CB_RAW) or Filter (Pt_CB_FILTER) callback.
The raw or filter callback must be defined for the widget whose region was passed to PhInitDrag(), not for the widget being dragged. For the example given, the Raw callback is defined for the base window. |
As described in “Event handlers — raw and filter callbacks” in the Editing Resources and Callbacks in PhAB chapter, you use an event mask to indicate which events your callback is to be called for. For dragging, the event is Ph_EV_DRAG. The most commonly used subtypes for this event are:
If you're doing outline dragging, the event subtype you're interested in is Ph_EV_DRAG_COMPLETE. When this event occurs, your callback should:
Remember, the callback's widget parameter is a pointer to the container (the base window in the example), not to the widget being dragged. Make sure you pass the correct widget to PtSetResources() or PtSetResource() when setting the Pt_ARG_POS resource. |
For example, here's the Raw callback for the outline dragging initiated above:
/* Raw callback to handle drag events; define this for the base window. */ /* Standard headers */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> /* Toolkit headers */ #include <Ph.h> #include <Pt.h> #include <Ap.h> /* Local headers */ #include "globals.h" #include "abimport.h" #include "proto.h" int end_dragging( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo ) { PhDragEvent_t *dragData; PhPoint_t new_pos; /* eliminate 'unreferenced' warnings */ widget = widget, apinfo = apinfo, cbinfo = cbinfo; /* Ignore all events until dragging is done. */ if (cbinfo->event->subtype != Ph_EV_DRAG_COMPLETE) { return (Pt_CONTINUE); } /* Get the data associated with the event. */ dragData = PhGetData (cbinfo->event); /* The rectangle in this data is the absolute coordinates of the dragging rectangle. We want to calculate the new position of the widget, relative to the dragging region. */ new_pos.x = dragData->rect.ul.x + cbinfo->event->translation.x; new_pos.y = dragData->rect.ul.y + cbinfo->event->translation.y; printf ("New position: (%d, %d)\n", new_pos.x, new_pos.y); /* Move the widget. */ PtSetResource (dragged_widget, Pt_ARG_POS, &new_pos, 0); return( Pt_CONTINUE ); }
The callback for opaque dragging is similar to that for outline dragging—the only difference is the subtype of event handled:
if (cbinfo->event->subtype != Ph_EV_DRAG_MOVE) { return (Pt_CONTINUE); }