blender/intern/ghost/intern/GHOST_SystemX11.cpp

839 lines
20 KiB
C++
Raw Normal View History

2002-10-12 11:37:38 +00:00
/**
* $Id$
* ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
*
* 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. The Blender
* Foundation also sells licenses for use in proprietary software under
* the Blender License. See http://www.blender.org/BL/ for information
* about this.
*
* 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.
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL/BL DUAL LICENSE BLOCK *****
*/
/**
* $Id$
* ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
*
* 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. The Blender
* Foundation also sells licenses for use in proprietary software under
* the Blender License. See http://www.blender.org/BL/ for information
* about this.
*
* 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.
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL/BL DUAL LICENSE BLOCK *****
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
2002-10-12 11:37:38 +00:00
#include "GHOST_SystemX11.h"
#include "GHOST_WindowX11.h"
#include "GHOST_WindowManager.h"
#include "GHOST_TimerManager.h"
#include "GHOST_EventCursor.h"
#include "GHOST_EventKey.h"
#include "GHOST_EventButton.h"
#include "GHOST_EventWheel.h"
2002-10-12 11:37:38 +00:00
#include "GHOST_DisplayManagerX11.h"
#include "GHOST_Debug.h"
#include <X11/Xatom.h>
#include <X11/keysym.h>
#ifdef __sgi
#if defined(_SGI_EXTRA_PREDEFINES) && !defined(NO_FAST_ATOMS)
#include <X11/SGIFastAtom.h>
#else
#define XSGIFastInternAtom(dpy,string,fast_name,how) XInternAtom(dpy,string,how)
#endif
#endif
2002-10-12 11:37:38 +00:00
// For timing
#include <sys/time.h>
#include <unistd.h>
#include <vector>
using namespace std;
GHOST_SystemX11::
GHOST_SystemX11(
) :
GHOST_System(),
m_start_time(0)
{
m_display = XOpenDisplay(NULL);
if (!m_display) return;
#ifdef __sgi
m_delete_window_atom
= XSGIFastInternAtom(m_display,
"WM_DELETE_WINDOW",
SGI_XA_WM_DELETE_WINDOW, False);
#else
m_delete_window_atom
= XInternAtom(m_display, "WM_DELETE_WINDOW", True);
#endif
2002-10-12 11:37:38 +00:00
// compute the initial time
timeval tv;
if (gettimeofday(&tv,NULL) == -1) {
GHOST_ASSERT(false,"Could not instantiate timer!");
}
m_start_time = GHOST_TUns64(tv.tv_sec*1000 + tv.tv_usec/1000);
}
GHOST_TSuccess
GHOST_SystemX11::
init(
){
GHOST_TSuccess success = GHOST_System::init();
if (success) {
m_keyboard_vector = new char[32];
m_displayManager = new GHOST_DisplayManagerX11(this);
if (m_keyboard_vector && m_displayManager) {
return GHOST_kSuccess;
}
}
return GHOST_kFailure;
}
GHOST_TUns64
GHOST_SystemX11::
getMilliSeconds(
) const {
timeval tv;
if (gettimeofday(&tv,NULL) == -1) {
GHOST_ASSERT(false,"Could not compute time!");
}
return GHOST_TUns64(tv.tv_sec*1000 + tv.tv_usec/1000) - m_start_time;
}
GHOST_TUns8
GHOST_SystemX11::
getNumDisplays(
) const {
return GHOST_TUns8(1);
}
/**
* Returns the dimensions of the main display on this system.
* @return The dimension of the main display.
*/
void
GHOST_SystemX11::
getMainDisplayDimensions(
GHOST_TUns32& width,
GHOST_TUns32& height
) const {
if (m_display) {
width = DisplayWidth(m_display, DefaultScreen(m_display));
height = DisplayHeight(m_display, DefaultScreen(m_display));
}
}
/**
* Create a new window.
* The new window is added to the list of windows managed.
* Never explicitly delete the window, use disposeWindow() instead.
* @param title The name of the window (displayed in the title bar of the window if the OS supports it).
* @param left The coordinate of the left edge of the window.
* @param top The coordinate of the top edge of the window.
* @param width The width the window.
* @param height The height the window.
* @param state The state of the window when opened.
* @param type The type of drawing context installed in this window.
* @return The new window (or 0 if creation failed).
*/
GHOST_IWindow*
GHOST_SystemX11::
createWindow(
const STR_String& title,
GHOST_TInt32 left,
GHOST_TInt32 top,
GHOST_TUns32 width,
GHOST_TUns32 height,
GHOST_TWindowState state,
GHOST_TDrawingContextType type,
bool stereoVisual
){
GHOST_WindowX11 * window = 0;
if (!m_display) return 0;
window = new GHOST_WindowX11 (
2004-08-24 03:30:16 +00:00
this,m_display,title, left, top, width, height, state, type, stereoVisual
2002-10-12 11:37:38 +00:00
);
if (window) {
// Install a new protocol for this window - so we can overide
// the default window closure mechanism.
XSetWMProtocols(m_display, window->getXWindow(), &m_delete_window_atom, 1);
if (window->getValid()) {
// Store the pointer to the window
m_windowManager->addWindow(window);
pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
}
else {
delete window;
window = 0;
}
}
return window;
}
GHOST_WindowX11 *
GHOST_SystemX11::
findGhostWindow(
Window xwind
) const {
if (xwind == 0) return NULL;
// It is not entirely safe to do this as the backptr may point
// to a window that has recently been removed.
// We should always check the window manager's list of windows
// and only process events on these windows.
vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
for (; win_it != win_end; ++win_it) {
GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
if (window->getXWindow() == xwind) {
return window;
}
}
return NULL;
}
static void SleepTillEvent(Display *display, GHOST_TInt64 maxSleep) {
2002-10-12 11:37:38 +00:00
int fd = ConnectionNumber(display);
fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
if (maxSleep == -1) {
select(fd + 1, &fds, NULL, NULL, NULL);
} else {
timeval tv;
tv.tv_sec = maxSleep/1000;
tv.tv_usec = (maxSleep - tv.tv_sec*1000)*1000;
select(fd + 1, &fds, NULL, NULL, &tv);
}
}
bool
GHOST_SystemX11::
processEvents(
bool waitForEvent
){
// Get all the current events -- translate them into
// ghost events and call base class pushEvent() method.
bool anyProcessed = false;
do {
GHOST_TimerManager* timerMgr = getTimerManager();
if (waitForEvent && m_dirty_windows.empty() && !XPending(m_display)) {
GHOST_TUns64 next = timerMgr->nextFireTime();
if (next==GHOST_kFireTimeNever) {
SleepTillEvent(m_display, -1);
} else {
SleepTillEvent(m_display, next - getMilliSeconds());
}
}
if (timerMgr->fireTimers(getMilliSeconds())) {
anyProcessed = true;
}
while (XPending(m_display)) {
XEvent xevent;
XNextEvent(m_display, &xevent);
processEvent(&xevent);
anyProcessed = true;
}
if (generateWindowExposeEvents()) {
anyProcessed = true;
}
} while (waitForEvent && !anyProcessed);
return anyProcessed;
}
void
GHOST_SystemX11::
processEvent(
XEvent *xe
){
GHOST_WindowX11 * window = findGhostWindow(xe->xany.window);
GHOST_Event * g_event = NULL;
if (!window) {
return;
}
switch (xe->type) {
case Expose:
{
XExposeEvent & xee = xe->xexpose;
if (xee.count == 0) {
// Only generate a single expose event
// per read of the event queue.
g_event = new
GHOST_Event(
getMilliSeconds(),
GHOST_kEventWindowUpdate,
window
);
}
break;
}
case MotionNotify:
{
XMotionEvent &xme = xe->xmotion;
g_event = new
GHOST_EventCursor(
getMilliSeconds(),
GHOST_kEventCursorMove,
window,
xme.x_root,
xme.y_root
);
break;
}
case KeyPress:
case KeyRelease:
{
XKeyEvent *xke = &(xe->xkey);
KeySym key_sym = XLookupKeysym(xke,0);
char ascii;
GHOST_TKey gkey = convertXKey(key_sym);
GHOST_TEventType type = (xke->type == KeyPress) ?
GHOST_kEventKeyDown : GHOST_kEventKeyUp;
if (!XLookupString(xke, &ascii, 1, NULL, NULL)) {
ascii = '\0';
}
g_event = new
GHOST_EventKey(
getMilliSeconds(),
type,
window,
gkey,
ascii
);
break;
}
2002-10-12 11:37:38 +00:00
case ButtonPress:
{
/* process wheel mouse events and break */
if (xe->xbutton.button == 4) {
g_event = new GHOST_EventWheel(getMilliSeconds(), window, 1);
break;
}
if (xe->xbutton.button == 5) {
g_event = new GHOST_EventWheel(getMilliSeconds(), window, -1);
break;
}
}
2002-10-12 11:37:38 +00:00
case ButtonRelease:
{
XButtonEvent & xbe = xe->xbutton;
GHOST_TButtonMask gbmask = GHOST_kButtonMaskLeft;
switch (xbe.button) {
case Button1 : gbmask = GHOST_kButtonMaskLeft; break;
case Button3 : gbmask = GHOST_kButtonMaskRight; break;
default:
case Button2 : gbmask = GHOST_kButtonMaskMiddle; break;
}
GHOST_TEventType type = (xbe.type == ButtonPress) ?
GHOST_kEventButtonDown : GHOST_kEventButtonUp;
g_event = new
GHOST_EventButton(
getMilliSeconds(),
type,
window,
gbmask
);
break;
}
// change of size, border, layer etc.
case ConfigureNotify:
{
/* XConfigureEvent & xce = xe->xconfigure; */
g_event = new
GHOST_Event(
getMilliSeconds(),
GHOST_kEventWindowSize,
window
);
break;
}
case FocusIn:
case FocusOut:
{
XFocusChangeEvent &xfe = xe->xfocus;
// May have to look at the type of event and filter some
// out.
GHOST_TEventType gtype = (xfe.type == FocusIn) ?
GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate;
g_event = new
GHOST_Event(
getMilliSeconds(),
gtype,
window
);
break;
}
case ClientMessage:
{
XClientMessageEvent & xcme = xe->xclient;
#ifndef __sgi
2002-10-12 11:37:38 +00:00
if (xcme.data.l[0] == m_delete_window_atom) {
g_event = new
GHOST_Event(
getMilliSeconds(),
GHOST_kEventWindowClose,
window
);
} else {
/* Unknown client message, ignore */
}
#endif
2002-10-12 11:37:38 +00:00
break;
}
// We're not interested in the following things.(yet...)
case NoExpose :
case GraphicsExpose :
case EnterNotify:
case LeaveNotify:
// XCrossingEvents pointer leave enter window.
break;
case MapNotify:
case UnmapNotify:
break;
case MappingNotify:
case ReparentNotify:
break;
default:
break;
}
if (g_event) {
pushEvent(g_event);
}
}
GHOST_TSuccess
GHOST_SystemX11::
getModifierKeys(
GHOST_ModifierKeys& keys
) const {
// analyse the masks retuned from XQueryPointer.
memset(m_keyboard_vector,0,sizeof(m_keyboard_vector));
2002-10-12 11:37:38 +00:00
XQueryKeymap(m_display,m_keyboard_vector);
// now translate key symobols into keycodes and
// test with vector.
const KeyCode shift_l = XKeysymToKeycode(m_display,XK_Shift_L);
const KeyCode shift_r = XKeysymToKeycode(m_display,XK_Shift_R);
const KeyCode control_l = XKeysymToKeycode(m_display,XK_Control_L);
const KeyCode control_r = XKeysymToKeycode(m_display,XK_Control_R);
const KeyCode alt_l = XKeysymToKeycode(m_display,XK_Alt_L);
const KeyCode alt_r = XKeysymToKeycode(m_display,XK_Alt_R);
// Shift
if ((m_keyboard_vector[shift_l >> 3] >> (shift_l & 7)) & 1) {
keys.set(GHOST_kModifierKeyLeftShift,true);
} else {
keys.set(GHOST_kModifierKeyLeftShift,false);
}
if ((m_keyboard_vector[shift_r >> 3] >> (shift_r & 7)) & 1) {
keys.set(GHOST_kModifierKeyRightShift,true);
} else {
keys.set(GHOST_kModifierKeyRightShift,false);
}
// control (weep)
if ((m_keyboard_vector[control_l >> 3] >> (control_l & 7)) & 1) {
keys.set(GHOST_kModifierKeyLeftControl,true);
} else {
keys.set(GHOST_kModifierKeyLeftControl,false);
}
if ((m_keyboard_vector[control_r >> 3] >> (control_r & 7)) & 1) {
keys.set(GHOST_kModifierKeyRightControl,true);
} else {
keys.set(GHOST_kModifierKeyRightControl,false);
}
// Alt (yawn)
if ((m_keyboard_vector[alt_l >> 3] >> (alt_l & 7)) & 1) {
keys.set(GHOST_kModifierKeyLeftAlt,true);
} else {
keys.set(GHOST_kModifierKeyLeftAlt,false);
}
if ((m_keyboard_vector[alt_r >> 3] >> (alt_r & 7)) & 1) {
keys.set(GHOST_kModifierKeyRightAlt,true);
} else {
keys.set(GHOST_kModifierKeyRightAlt,false);
}
return GHOST_kSuccess;
}
GHOST_TSuccess
GHOST_SystemX11::
getButtons(
GHOST_Buttons& buttons
) const {
Window root_return, child_return;
int rx,ry,wx,wy;
unsigned int mask_return;
if (XQueryPointer(
m_display,
RootWindow(m_display,DefaultScreen(m_display)),
&root_return,
&child_return,
&rx,&ry,
&wx,&wy,
&mask_return
) == False) {
return GHOST_kFailure;
} else {
if (mask_return & Button1Mask) {
buttons.set(GHOST_kButtonMaskLeft,true);
} else {
buttons.set(GHOST_kButtonMaskLeft,false);
}
if (mask_return & Button2Mask) {
buttons.set(GHOST_kButtonMaskMiddle,true);
} else {
buttons.set(GHOST_kButtonMaskMiddle,false);
}
if (mask_return & Button3Mask) {
buttons.set(GHOST_kButtonMaskRight,true);
} else {
buttons.set(GHOST_kButtonMaskRight,false);
}
}
return GHOST_kSuccess;
}
GHOST_TSuccess
GHOST_SystemX11::
getCursorPosition(
GHOST_TInt32& x,
GHOST_TInt32& y
) const {
Window root_return, child_return;
int rx,ry,wx,wy;
unsigned int mask_return;
if (XQueryPointer(
m_display,
RootWindow(m_display,DefaultScreen(m_display)),
&root_return,
&child_return,
&rx,&ry,
&wx,&wy,
&mask_return
) == False) {
return GHOST_kFailure;
} else {
x = rx;
y = ry;
}
return GHOST_kSuccess;
}
GHOST_TSuccess
GHOST_SystemX11::
setCursorPosition(
GHOST_TInt32 x,
GHOST_TInt32 y
) const {
// This is a brute force move in screen coordinates
// XWarpPointer does relative moves so first determine the
// current pointer position.
int cx,cy;
if (getCursorPosition(cx,cy) == GHOST_kFailure) {
return GHOST_kFailure;
}
int relx = x-cx;
int rely = y-cy;
XWarpPointer(m_display,None,None,0,0,0,0,relx,rely);
XFlush(m_display);
return GHOST_kSuccess;
}
void
GHOST_SystemX11::
addDirtyWindow(
GHOST_WindowX11 * bad_wind
){
GHOST_ASSERT((bad_wind != NULL), "addDirtyWindow() NULL ptr trapped (window)");
m_dirty_windows.push_back(bad_wind);
}
bool
GHOST_SystemX11::
generateWindowExposeEvents(
){
vector<GHOST_WindowX11 *>::iterator w_start = m_dirty_windows.begin();
vector<GHOST_WindowX11 *>::const_iterator w_end = m_dirty_windows.end();
bool anyProcessed = false;
for (;w_start != w_end; ++w_start) {
GHOST_Event * g_event = new
GHOST_Event(
getMilliSeconds(),
GHOST_kEventWindowUpdate,
*w_start
);
(*w_start)->validate();
if (g_event) {
pushEvent(g_event);
anyProcessed = true;
}
}
m_dirty_windows.clear();
return anyProcessed;
}
#define GXMAP(k,x,y) case x: k = y; break;
GHOST_TKey
GHOST_SystemX11::
convertXKey(
unsigned int key
){
GHOST_TKey type;
if ((key >= XK_A) && (key <= XK_Z)) {
type = GHOST_TKey( key - XK_A + int(GHOST_kKeyA));
} else if ((key >= XK_a) && (key <= XK_z)) {
type = GHOST_TKey(key - XK_a + int(GHOST_kKeyA));
} else if ((key >= XK_0) && (key <= XK_9)) {
type = GHOST_TKey(key - XK_0 + int(GHOST_kKey0));
} else if ((key >= XK_F1) && (key <= XK_F24)) {
type = GHOST_TKey(key - XK_F1 + int(GHOST_kKeyF1));
} else {
switch(key) {
GXMAP(type,XK_BackSpace, GHOST_kKeyBackSpace);
GXMAP(type,XK_Tab, GHOST_kKeyTab);
GXMAP(type,XK_Return, GHOST_kKeyEnter);
GXMAP(type,XK_Escape, GHOST_kKeyEsc);
GXMAP(type,XK_space, GHOST_kKeySpace);
GXMAP(type,XK_Linefeed, GHOST_kKeyLinefeed);
GXMAP(type,XK_semicolon, GHOST_kKeySemicolon);
GXMAP(type,XK_period, GHOST_kKeyPeriod);
GXMAP(type,XK_comma, GHOST_kKeyComma);
GXMAP(type,XK_quoteright, GHOST_kKeyQuote);
GXMAP(type,XK_quoteleft, GHOST_kKeyAccentGrave);
GXMAP(type,XK_minus, GHOST_kKeyMinus);
GXMAP(type,XK_slash, GHOST_kKeySlash);
GXMAP(type,XK_backslash, GHOST_kKeyBackslash);
GXMAP(type,XK_equal, GHOST_kKeyEqual);
GXMAP(type,XK_bracketleft, GHOST_kKeyLeftBracket);
GXMAP(type,XK_bracketright, GHOST_kKeyRightBracket);
GXMAP(type,XK_Pause, GHOST_kKeyPause);
GXMAP(type,XK_Shift_L, GHOST_kKeyLeftShift);
GXMAP(type,XK_Shift_R, GHOST_kKeyRightShift);
GXMAP(type,XK_Control_L, GHOST_kKeyLeftControl);
GXMAP(type,XK_Control_R, GHOST_kKeyRightControl);
GXMAP(type,XK_Alt_L, GHOST_kKeyLeftAlt);
GXMAP(type,XK_Alt_R, GHOST_kKeyRightAlt);
GXMAP(type,XK_Insert, GHOST_kKeyInsert);
GXMAP(type,XK_Delete, GHOST_kKeyDelete);
GXMAP(type,XK_Home, GHOST_kKeyHome);
GXMAP(type,XK_End, GHOST_kKeyEnd);
GXMAP(type,XK_Page_Up, GHOST_kKeyUpPage);
GXMAP(type,XK_Page_Down, GHOST_kKeyDownPage);
GXMAP(type,XK_Left, GHOST_kKeyLeftArrow);
GXMAP(type,XK_Right, GHOST_kKeyRightArrow);
GXMAP(type,XK_Up, GHOST_kKeyUpArrow);
GXMAP(type,XK_Down, GHOST_kKeyDownArrow);
GXMAP(type,XK_Caps_Lock, GHOST_kKeyCapsLock);
GXMAP(type,XK_Scroll_Lock, GHOST_kKeyScrollLock);
GXMAP(type,XK_Num_Lock, GHOST_kKeyNumLock);
/* keypad events */
GXMAP(type,XK_KP_0, GHOST_kKeyNumpad0);
GXMAP(type,XK_KP_1, GHOST_kKeyNumpad1);
GXMAP(type,XK_KP_2, GHOST_kKeyNumpad2);
GXMAP(type,XK_KP_3, GHOST_kKeyNumpad3);
GXMAP(type,XK_KP_4, GHOST_kKeyNumpad4);
GXMAP(type,XK_KP_5, GHOST_kKeyNumpad5);
GXMAP(type,XK_KP_6, GHOST_kKeyNumpad6);
GXMAP(type,XK_KP_7, GHOST_kKeyNumpad7);
GXMAP(type,XK_KP_8, GHOST_kKeyNumpad8);
GXMAP(type,XK_KP_9, GHOST_kKeyNumpad9);
GXMAP(type,XK_KP_Decimal, GHOST_kKeyNumpadPeriod);
GXMAP(type,XK_KP_Insert, GHOST_kKeyNumpad0);
GXMAP(type,XK_KP_End, GHOST_kKeyNumpad1);
GXMAP(type,XK_KP_Down, GHOST_kKeyNumpad2);
GXMAP(type,XK_KP_Page_Down, GHOST_kKeyNumpad3);
GXMAP(type,XK_KP_Left, GHOST_kKeyNumpad4);
GXMAP(type,XK_KP_Begin, GHOST_kKeyNumpad5);
GXMAP(type,XK_KP_Right, GHOST_kKeyNumpad6);
GXMAP(type,XK_KP_Home, GHOST_kKeyNumpad7);
GXMAP(type,XK_KP_Up, GHOST_kKeyNumpad8);
GXMAP(type,XK_KP_Page_Up, GHOST_kKeyNumpad9);
GXMAP(type,XK_KP_Delete, GHOST_kKeyNumpadPeriod);
GXMAP(type,XK_KP_Enter, GHOST_kKeyNumpadEnter);
GXMAP(type,XK_KP_Add, GHOST_kKeyNumpadPlus);
GXMAP(type,XK_KP_Subtract, GHOST_kKeyNumpadMinus);
GXMAP(type,XK_KP_Multiply, GHOST_kKeyNumpadAsterisk);
GXMAP(type,XK_KP_Divide, GHOST_kKeyNumpadSlash);
/* some extra sun cruft (NICE KEYBOARD!) */
#ifdef __sun__
GXMAP(type,0xffde, GHOST_kKeyNumpad1);
GXMAP(type,0xffe0, GHOST_kKeyNumpad3);
GXMAP(type,0xffdc, GHOST_kKeyNumpad5);
GXMAP(type,0xffd8, GHOST_kKeyNumpad7);
GXMAP(type,0xffda, GHOST_kKeyNumpad9);
GXMAP(type,0xffd6, GHOST_kKeyNumpadSlash);
GXMAP(type,0xffd7, GHOST_kKeyNumpadAsterisk);
#endif
default :
type = GHOST_kKeyUnknown;
break;
}
}
return type;
}
#undef GXMAP