Method and apparatus for enhancing the portability of an object oriented interface among multiple platforms5999728Abstract The present invention is directed to providing an ability to re-host, or port, an object oriented graphical user interface implementation from a native window-based platform, or environment, to another window-based platform, or environment. Exemplary embodiments abstract any notifications (e.g., events, state changes or "interests") which occur in the native environment as behavioral specifications. These behavioral specifications, (i.e., traits or protocols) can be used as part of a conformance negotiation to determine, during the execution lifetime of the graphical user interface, whether a particular client side object will conform with the behavioral specifications which have been abstracted from server side events associated with a different object. Where the conformance negotiation proves successful, abstract notifications can flow between particular instances of objects to model the state of the system, rather than using native implementations of events. During the execution lifetime, other objects can dynamically establish relationships with object classes containing abstracted notifications for the purpose of receiving the abstracted notifications. Claims What is claimed is: Description BACKGROUND OF THE INVENTION
______________________________________
typedef struct {
int type; // == ButtonPress
unsigned long serial;
// serial number of last request processed
Bool send.sub.-- event;
// synthetically generated
Display*
display; // window server client reference
Window window; // the window the event is delivered to
Window root; // root window of screen
Window subwindow;
// the window in which the event
occurred
Time time; // server timestamp
int x, // pixel based, origin top left
y;
int x.sub.-- root,
// screen relative coords
y.sub.-- root;
unsigned int state;
// state of keybd modifiers
unsigned int button;
// which button is pressed?
Bool same.sub.-- screen;
} XButtonEvent;
typedef XButtonEvent XButtonPressedEvent;
typedef XButtonEvent XButtonReleasedEvent;
/* constant for "type" field above */
#define ButtonPress
4
/* constants for "state" field above */
#define ShiftMask
(1 < < 0)
#define LockMask
(1 < < 1)
#define ControlMask
(1 < < 2)
#define Mod1Mask
(1 < < 3)
#define Mod2Mask
(1 < < 4)
#define Mod3Mask
(1 < < 5)
#define Mod4Mask
(1 < < 6)
#define Mod5Mask
(1 < < 7)
/* constants for "button" field above */
#define Button1
1
#define Button2
2
#define Button3
3
#define Button4
4
#define Button5
5
______________________________________
In the NeXTSTEP Window System the Button event is defined as follows (for details, see "NeXTSTEP General Reference (volume 2)" by NeXT Computer Inc, Addison Wesley, 1992 ISBN 0-201-62221-1):
______________________________________
/* EventData type: defines the data field of an event */
typedef union {
struct { /* For mouse events */
short reserved;
short eventNum; /* unique identifier for this
button */
int click; /* click state of this event */
unsigned char
pressure; /* pressure value:
0=none,
255=full */
char reserved1;
short reserved2;
} mouse;
/* other event type data deteted for clarity */
} NXEventData;
/* The event record! */
typedef struct {
float x;
float y;
} NXPoint;
typedef struct.sub.-- NXEvent {
int type; /* An event type from
above */
NXPoint location; /* Base coordinates in
window, from lower-left */
long time; /* vertical intervals since
launch */
int flags; /* key state flags */
unsigned int window; /* window number of
assigned window */
NXEventData data; /* type-dependent data */
DPSContext ctxt; /* context the event came
from */
} NXEvent, *NXEventPtr;
/* constants for "type" field above */
#define NX.sub.-- LMOUSEDOWN
1 /* left mouse-down
event */
#define NX.sub.-- LMOUSEUP
2 /* left mouse-up
event */
#define NX.sub.-- RMOUSEDOWN
3 /* right mouse-down
event */
#define NX.sub.-- RMOUSEUP
4 /* right mouse-up
event */
#define NX.sub.-- MOUSEMOVED
5 /* mouse-moved
event */
#define NX.sub.-- LMOUSEDRAGGED
6 /* left mouse-dragged
event */
#define NX.sub.-- RMOUSEDRAGGED
7 /* right
mouse-dragged event */
#define NX.sub.-- MOUSEDOWN
NX.sub.-- LMOUSEDOWN
/* Synonym */
#define NX.sub.-- MOUSEUP
NX.sub.-- LMOUSEUP
/* Synonym */
#define NX.sub.-- MOUSEDRAGGED
NX.sub.-- LMOUSEDRAGGED
/* Synonym */
/* constants for "flags" field above */
#define NX.sub.-- ALPHASHIFTMASK
(1 << 16) /* if alpha
lock is on
#define NX.sub.-- SHIFTMASK
(1 << 17) /* if shift key
is down
#define NX.sub.-- CONTROLMASK
(1 << 18) /* if control
key is down */
#define NX.sub.-- ALTERNATEMASK
(1 << 19) /* if alt key is
down
#define NX.sub.-- COMMANDMASK
(1 << 20) /* if command
key is down */
#define NX.sub.-- NUMERICPADMASK
(1 << 21) /* if key on
numeric pad */
#define NX.sub.-- HELPMASK
(1 << 22) /* if help key
is down
/* Device-dependent bits */
#define NX.sub.-- NEXTCTRLKEYMASK
(1 << 0) /* control key
#define NX.sub.-- NEXTLSHIFTKEYMASK
(1 << 1) /* leftside
shift key
#define NX.sub.-- NEXTRSHIFTKEYMASK
(1 << 2) /* right side
shift key
#define NX.sub.-- NEXTLCMDKEYMASK
(1 << 3) /* leftside
command key */
#define NX.sub.-- NEXTRCMDKEYMASK
(1 << 4) /* right side
command key */
#define NX.sub.-- NEXTLALTKEYMASK
(1 << 5) /* left side alt
key
#define NX.sub.-- NEXTRALTKEYMASK
(1 << 6) /* right side
alt key
#define NX.sub.-- STYLUSPROXIMITYMASK
(1 << 7) /* if stylus is
in proximity
* (for tablets) */
#define NX.sub.-- NONCOALSESCEDMASK
(1 << 8) /* event was
generated with event
coalescing disabled */
______________________________________
It is apparent from the definitions above that the two implementations differ significantly in both their concrete syntax and semantics. Thus, these definitions cannot be used in their native form to create an implementation of a graphical user interface toolkit that is portable between both platforms without comprehensive conditional compilation directives to create parallel implementations for each target window system. However, in accordance with exemplary embodiments of the present invention, a common definition for a button press event can be abstracted that will be portable across both window systems, making the vast majority of the code implementing the toolkit platform independent, through the localization of the platform dependent code into a single, or relatively few components of the toolkit. Given the concrete window system event notifications defined above, a first step of an implementation according to the exemplary embodiments is to define a common abstraction of the concrete events. This example will use the Objective-C programming language since this is the implementation language used in programming systems such as NeXTSTEP and OpenStep (for the X Window System). An abstract event is used to define the event type, its source, and other fundamental information common to all event abstractions:
______________________________________
@interface AbstractEvent : Object
enum {
AEButtonPress,
AEButtonRelease,
AEMouseMoved,
// ...
} eventType;
SystemEventDispatcher
eventDispatcher; // object
that maps and dispatches
events from the platform
window system. //
unsigned int eventSerial; // serial number
unsigned long
eventTimestamp; // system
timestamp
}
// ...
@end
______________________________________
Next, the concept of an event associated with a "window" is introduced; that is, a concept common to both target platforms:
______________________________________
@interface AbstractWindowEvent : AbstractEvent
unsigned int eventWindow;
// id of window event
occurred on
}
// ...
@end
______________________________________
Finally, a particular abstraction is defined for a button press event, containing information relevant to both target platforms, but with a synthesized syntax and semantics distinct from those found in the concrete events:
______________________________________
typedef enum {
AKbdEShiftModifier = (1 < 0),
AKbdECtrlModifier = (1 < 1),
// ...
} AKbdEventModifiers;
@interface AbstractButtonPressEvent : AbstractWindowEvent
float eventX;
float eventY;
enum {
ABPELeftButton,
ABPEMiddleButton,
ABPERightButton
} eventButton;
AKbdEventModifiers
eventKbdModifiers;
}
// ...
@end
______________________________________
A hierarchy of abstract window system event objects, recognizable to those skilled in the art, has thus been defined as containing a synthesis of the information found in the concrete event systems of the X Window System and the NeXTSTEP Window System for mouse button press events. Next, an interface to an object hierarchy is defined that allows an arbitrary object (the observer or sink) to express an interest in observing notifications from another object (the observee or source) via a particular protocol or interface specification:
______________________________________
@interface Object(InterestProtocolRegistration)
(Bool) addObserver: (Object *) observer
forProtocol: (Protocol*) interestProtocol;
(Bool) removeObserver: (Object *) observer
forProtocol: (Protocol*) interestProtocol;
@end
______________________________________
A set of protocols is then defined based upon the abstract event definitions. These protocols describe the formal interface between arbitrary objects that may emit and receive notifications of changes in state of the system as represented by the abstract event descriptions detailed above.
______________________________________
@protocol AbstractEventObserverProtocol
(void) gotEvent: (AbstractEvent*) theEvent
fromSource: (Object *) theEventSource;
@end
@protocol AbstractWindowEventObserverProtocol
(void) gotWindowEvent: (AbstractWindowEvent*)
theWindowEvent
fromSource: (Object *) theEventSource;
@end
@protocol AbstractButtonPressEventObserverProtocol
(void) gotButtonPressEvent: (AbstractButtonPressEvent*)
theWindowEvent
fromSource: (Object
*)
theEventSource;
@end
______________________________________
Having defined these abstract protocols, a "ButtonControl" object that wishes to receive these notifications can be defined:
______________________________________
@interface ButtonControl : Control
<AbstractButtonPressEventObserverProtocol,
AbstractWindowEventObserverProtocol,
// ...
>
// ...
(ButtonControl) newButtonControl;
(void) dealloc;
(void) gotButtonPressEvent: (AbstractButtonPressEvent*)
theWindowEvent
fromSource: (Object
*)
theEventSource;
(void) gotwindowEvent: (AbstractWindowEvent*)
theWindowEvent
fromSource: (Object
*)
theEventSource;
// ...
@end
Relevant parts of the implementation are then provided:
@implementation ButtonControl
(ButtonControl) newButtonControl
______________________________________
This method creates a new instance of a ButtonControl. As a side effect, the ButtonControl registers itself with the toolkit's "eventDispatcher" object to receive notifications of ButtonPress events. self=[super init]; Now, this object is registered with the window system event dispatcher in order to receive the protocol notifications.
______________________________________
[[SystemEventDispatcher eventDispatcher]
addObserver: self
forProtocol:
@protocol(AbstractButtonPressEventObserverProtocol)
];
return (ButtonControl*)self;
(void) dealloc
{
______________________________________
This method is called to free instances of the ButtonControl. As a side effect, the ButtonControl removes itself from the eventDispatcher object:
______________________________________
[[SystemEventDispatcher eventDispatcher]
removeObserver: self
forProtocol:
@protocol(AbstractButtonPressEventObserverProtocol)
];
[super dealloc];
(void) gotButtonPressEvent: (AbstractButtonPressEvent*)
thewindowEvent
fromSource: (Object*
theEventSource
{
// this method get invoked when a Button Press occurs
on this
// Control ...
// .... do ButtonControl activation processing here ...
}
@end
______________________________________
Having defined the graphical user interface toolkit objects in terms of these abstract protocols, the "SystemEventDispatcher" class is implemented. For the present example, this is the only class in the event subsystem of the graphical user interface toolkit that includes window system dependent code:
______________________________________
@interface SystemEventDispatcher : Object
+ (SystemEventDispatcher) eventDispatcher;
(void) run;
(Bool) addObserver: (Object *) observer
forProtocol: (Protocol*) interestProtocol;
(Bool) removeObserver: (Object *) observer
orProtocol: (Protocol*) interestProtocol;
(void) dispatchAbstractEvent: (AbstractEvent*) absev;
@end
______________________________________
A class private interface to the SystemEventDispatcher is defined that encapsulates all of the platform dependent code:
______________________________________
@interface
SystemEventDispatcher
(PlatformDependentStuff)
@private
#ifdef XWindows
(XEvent) .sub.-- nextXEvent; // fetches
next XEvent from X
Window server
(void) .sub.-- mapXEvent: (XEvent* ) xEvent
OntoAbstractEvent: (AbstractEvent **)
returnEvent;
// ...
#endif
#ifdef NeXTSTEP
(NXEvent) .sub.-- nextNXEvent;
// fetches next NXEvent from
NeXTSTEP DPS
(void) .sub.-- mapNXEvent: (NXEvent* )
nxEvent
OntoAbstractEvent: (AbstractEvent **)
return Event;
// ...
#endif
// ...
@end
@implementation SystemEventDispatcher
// ...
HashTable* button PressEventObservers;
// contains all
Controls
// interested in
button
// press events
hashed by
// window id.
// ...
}
+ (SystemEventDispatcher) eventDispatcher
{
// create a new instance of a system event
dispatcher
static SystemEventDispatcher*
eventDispatcher = nil;
if (eventDispatcher = = nil) {
eventDispatcher = [[SystemEventDispatcher
alloc] init];
}
return eventDispatcher;
}
run
{
// run the event dispatcher, fetching events from
the system,
// mapping them from the platform specific
form into the
// abstract mapping and subsequently
dispatching them to all
// eligble observers ...
for (;;) {
AbstractEvent* absev;
#ifdef XWindows
[self .sub.-- mapXEvent: [self .sub.-- nextXEvent]
OntoAbstractEvent: &absev
];
#endif
#ifdef NeXTSTEP
[self .sub.-- mapNXEvent: [self .sub.-- nextNXEvent]
OntoAbstractEvent: &absev
];
#endif
[self dispatchAbstractEvent: absev];
}
}
(void) dispatchAbstractEvent:
(AbstractEvent*) absev
{
// dispatch abstract events to eligible observers
switch ([absev eventType] {
// ...
case AEButtonPress:
// Find the COntrol associated with the
window id
// of the abstract event and dispatch the
event
// to it ...
Control* c = [button PressEventObservers
valueForKey: [absev
eventWindow]
];
[c gotButtonPressEvent: absev
FromSource: self
];
break;
// ...
}
}
(Bool) addObserver: Object* observer
forProtocol: Protocol* interestProtocol
{
// register observer (if eligble) for particular
observer protocol
// ...
if (interestProtocol = =
@protocol
(AbstractButtonPressEventObserverProtocol)
&&
[observer conformsTo: interestProtocol]) {
// if the observer conforms to the protocol
save it in a
// hash table indexed by its window
identifier.
[buttonPressEventObservers
insertKey: (const void *) [observer
window];
value: (void *) observer
];
}
// ...
return (Bool)True;
}
// ...
@end
@implementation
SystemEventDispatcher
(PlatformDependentStuff)
#ifdef XWindows
// ...
(void) .sub.-- mapXEvent: (XEvent* ) xEvent
OntoAbstractEvent: (AbstractEvent **)
returnEvent
{
// platform dependent code to map system
events onto event abstractions
switch (xEvent->type) {
// ...
case ButtonPress: {
XButtonPressedEvent* xPress =
(XButtonpressedEvent*)xEvent;
// create a new AbstractButtonPressEvent
*returnEvent = [AbstractButtonPressEvent
new];
// here we initialize the member vars of
the
// AbstractButtonPressEvent based upon
the
// platform dependent information found
in the
// XButtonPressedEvent
// ...
}
break;
// ...
}
}
// ...
#endif
#ifdef NeXTSTEP
// ...
#endif
@end
______________________________________
A simple exemplary "main" program demonstrates a ButtonControl that will receive abstract notifications of button presses from the SystemEventDispatcher:
______________________________________
int main(const int argc, const char **const argv)
[ButtonControl newButtoncontrol]; // create a
ButtonControl
[[SystemEventDispatcher eventDispatcher] run]; // run
the dispatcher
}
______________________________________
In accordance with the present invention, the abstract notification of behavioral specifications, in conjunction with the event dispatch object, provides an ability to runtime negotiate whether a particular object in the graphical user interface toolkit conforms to the notification, as described with respect to step 208 of FIG. 2. The ability to provide runtime conformance testing, referred to herein as runtime negotiation, allows relationships between objects in a view field versus objects in a control or model field to change dynamically during the execution lifetime of the system. In the exemplary FIG. 3 system, runtime negotiation allows relationships to be dynamically altered (i.e., established and de-established) via a remapping within the event dispatch object. To better illustrate the foregoing features, and in particular the runtime negotiation feature, consider two anonymous objects: one in a window-based platform and the other in the graphical user interface. The two objects know only of each other's existence and nothing else. The two objects will be referenced herein as a "vendor" and as a "customer". A vendor 402 and a customer 404 are graphically illustrated in FIG. 4. Assuming that the behavioral specification of the vender includes a protocol which the customer (e.g., the customer of a client-side application) wants to use, such as a keypress event, the vender must be able to export some notification of keypress events to the customer. The customer knows of the vender's existence, and is interested in entering a runtime negotiation to verify its ability to pass information (e.g., notifications) to the vender. The customer therefore needs to know if the vender conforms with the customer's behavioral specifications. Given this starting position, the runtime negotiation is initiated by the customer querying the vender as to whether it vends the behavioral specification "A" (i.e., the protocol which the customer wants to use). If the vender does not vend the behavioral specification "A", the customer discontinues its attempt to establish an information transfer with the vender. However, if the vender indicates that it does vend the behavioral specification "A", the customer registers an "interest" with the vender for the behavioral specification corresponding to "A". Because the vender supports this behavioral specification, the customer informs the vender that it wants to participate in this behavioral specification with the vender so that the vender can transfer information to a view of the customer, whenever state changes associated with this behavioral specification occur. Once the customer confirms that it wants to participate in the behavioral specification and negotiate with the vender to receive notifications regarding state changes, the vender queries the customer as to whether it conforms (i.e., adheres) to the behavioral specification "A". Assuming that the customer does conform with the behavioral specification "A", a relationship (i.e., runtime contract) is established, such that abstract notifications can flow between these two objects. In accordance with exemplary embodiments, at any time during the execution life of the system, the vender and/or customer can withdraw from the relationship. That is, either object can remove itself from the relationship. Such a removal may be desirable if, for example, the customer completes its job and resigns from the relationship. If either object resigns from the relationship, then new relationships can be established in the manner already described. Having described an exemplary runtime negotiation, attention is now directed to an exemplary mapping of objects to one another in accordance with the present invention. In this regard, reference is made to FIG. 5, wherein an event corresponding to the activation of a push button on a display of a window-based system is to be used to trigger an event. In accordance with exemplary embodiments, concrete information corresponding to this event is mapped from the window-based platform into abstract information by the graphical user interface in the manner described previously. Here, a push button 502 is represented in a client-side object oriented graphical user interface as an object with corresponds to both a view and to a controller of the model-view-controller paradigm. As a view, it is responsible for drawing the push button on a display. As a controller, it describes state changes which occur to indicate that the push button is depressed or released. Assume that the behavioral specification for the push button includes a protocol "Button Clicks", that produces two messages: (1) push button depressed; and (2) push button released. In the case of a push button, the abstracted notification can further include information which identifies the time at which it was depressed (i.e., a time stamp), keyboard modifiers which were activated with the push button (e.g., activation of the shift key with a letter key), or the position of a mouse at the time of push button activation (e.g., where the button on the mouse was activated). Assume further that the event dispatch object vends the "Button Clicks" protocol. During a runtime negotiation, the push button queries the event dispatch object as to whether it vends the "Button Clicks" protocol. If the event dispatch object 504 does vend this protocol, the push button responds by indicating that it wants to participate in the "Button Clicks" protocol with the event dispatch object. The push button therefore registers an "interest" in the "Button Clicks" protocol. The event dispatch object next queries the push button as to whether it conforms to the "Button Clicks" protocol. If the push button answers affirmatively, a relationship is established. As such, when the event dispatch object receives a button press event from the window-based platform 506, it then notifies the push button of a "push button depress" event. Because exemplary embodiments of the present invention abstract events, certain information which is implementation specific may, of course, be lost. However, in return for any lost information enhanced portability of the system is achieved. Further, as those skilled in the art will appreciate, any implementation specific information can be retained by specifically encoding this in the client-side object oriented graphical user interface for the platform specific implementation. Referring again to FIG. 5, those skilled in the art will appreciate that when notifications are abstracted as described above, a 1:1 relationship between objects need not be maintained. Rather, all of the objects having an "interest" in the abstract notification can be mapped to receive the notification. For example, where the abstract notification corresponds to the output of a timer 508, an associated protocol "tick" can be established to inform the event dispatch object every time one second passes. A separate "Timeflow" protocol can then be established between the event dispatch object and all objects (e.g., objects 510 and 512) having an "interest" in tracking time. Such objects may include, for example, a clock on the display, a stock market ticker feed, a timer and so forth. The views of all objects having an interest in tracking time are thus notified of changes in time via the "Timeflow" protocol. Accordingly, even though the "tick" protocol has a 1:1 relationship with the event dispatch object, the "Timeflow" protocol represents a relationship of the event dispatch object with all objects having an interest in tracking time. Those skilled in the art will appreciate that exemplary embodiments of the present invention can be implemented in many different ways. For example, exemplary embodiments can be used to port features of a graphical user interface to a window-based system implemented in either software or hardware. Further, although exemplary embodiments have been described in the context of a client server system, those skilled in the art will appreciate that features of the invention can be used to port a toolkit of a graphical user interface to any window-based system(s) regardless of where the window-based system(s) resides. It will be appreciated by those skilled in the art that the present invention can be embodied in other specific forms without departing from the spirit or essential characteristics thereof. The presently disclosed embodiments are therefore considered in all respects to be illustrative and not restricted. The scope of the invention is indicated by the appended claims rather than the foregoing description and all changes that come within the meaning and range and equivalence thereof are intended to be embraced therein.
|
Same subclass Same class Consider this |
||||||||||
