/* (c) Copyright 2002-2003 Denis Oliver Kropp All rights reserved. XDirectFB is mainly based on XDarwin and also contains some KDrive, XFree and XWin code. */ /************************************************************** * * Shared code for the DirectFB X Server * running with DirectFB or the IOKit * **************************************************************/ /* $XFree86: xc/programs/Xserver/hw/directfb/directfb.c,v 1.44 2002/02/05 19:16:13 torrey Exp $ */ #include "X.h" #include "Xos.h" #include "Xproto.h" #include "os.h" #include "servermd.h" #include "inputstr.h" #include "scrnintstr.h" #include "mibstore.h" #include "mipointer.h" #include "micmap.h" #include "fb.h" #include "site.h" #include "globals.h" #include "dix.h" #ifdef DPMSExtension #define DPMS_SERVER #include "extensions/dpms.h" #include "dpmsproc.h" #endif #include #include #include #include #include #include "directfbX.h" #include "directfbKeyboard.h" #include "directfbScreen.h" #include "directfbDGA.h" #include "rootlessDirectFB.h" /* Fake button press/release for scroll wheel move. */ #define SCROLLWHEELUPFAKE 4 #define SCROLLWHEELDOWNFAKE 5 /* * X server shared global variables */ /* location of X11s (0,0) point in global screen coordinates */ int directfbMainScreenX = 0; int directfbMainScreenY = 0; /* parameters read from the command line or user preferences */ XDirectFBPrefs directfbPrefs = { 0xff, /* default */ 0xb0, /* unfocused */ FALSE, /* enable unfocused */ FALSE, /* enable fade in */ FALSE, /* enable fade out */ FALSE, /* enable root window */ DLID_PRIMARY }; int directfbEventsPipe[2] = { -1, -1}; DFBInputDeviceLockState directfbLocks = 0; DFBInputDeviceLockState directfbLocksDown = 0; static DeviceIntPtr directfbPointer; static DeviceIntPtr directfbKeyboard; /* Common pixmap formats */ static PixmapFormatRec formats[] = { { 1, 1, BITMAP_SCANLINE_PAD}, { 4, 8, BITMAP_SCANLINE_PAD}, { 8, 8, BITMAP_SCANLINE_PAD}, { 15, 16, BITMAP_SCANLINE_PAD}, { 16, 16, BITMAP_SCANLINE_PAD}, { 24, 32, BITMAP_SCANLINE_PAD}, { 32, 32, BITMAP_SCANLINE_PAD} }; const int NUMFORMATS = sizeof(formats)/sizeof(formats[0]); static void XDirectFBPrintBanner(void) { ErrorF("XDirectFB / X Window System\n"); ErrorF("(protocol Version %d, revision %d, vendor release %d)\n", X_PROTOCOL, X_PROTOCOL_REVISION, VENDOR_RELEASE ); ErrorF("\tIf the server is older than 6-12 months, or if your hardware is\n" "\tnewer than the above date, look for a newer version before\n" "\treporting problems. (See http://www.XFree86.Org/FAQ)\n"); #if defined(BUILDERSTRING) ErrorF("%s \n",BUILDERSTRING); #endif } /* ============================================================================= mouse and keyboard callbacks ============================================================================= */ /* * DirectFBChangePointerControl * Set mouse acceleration and thresholding */ static void XDirectFBChangePointerControl (DeviceIntPtr device, PtrCtrl *ctrl) { IDirectFBDisplayLayer *layer; if (!dfb) return; if (dfb->GetDisplayLayer (dfb, directfbPrefs.layerID, &layer)) return; if (layer->SetCooperativeLevel (layer, DLSCL_ADMINISTRATIVE) == DFB_OK) layer->SetCursorAcceleration (layer, ctrl->num, ctrl->den, ctrl->threshold); layer->Release (layer); } /* * DirectFBMouseProc * Handle the initialization, etc. of a mouse */ static int XDirectFBMouseProc (DeviceIntPtr pPointer, int what) { char map[6]; switch (what) { case DEVICE_INIT: pPointer->public.on = FALSE; /* Set button map. */ map[1] = 1; map[2] = 2; map[3] = 3; map[4] = 4; map[5] = 5; InitPointerDeviceStruct( (DevicePtr)pPointer, map, 5, /* numbuttons (4 & 5 are scroll wheel) */ miPointerGetMotionEvents, XDirectFBChangePointerControl, 0 ); break; case DEVICE_ON: pPointer->public.on = TRUE; AddEnabledDevice (directfbEventsPipe[0]); return Success; case DEVICE_CLOSE: case DEVICE_OFF: RemoveEnabledDevice (directfbEventsPipe[0]); pPointer->public.on = FALSE; return Success; } return Success; } /* * DirectFBKeybdProc * Callback from X */ static int XDirectFBKeybdProc( DeviceIntPtr pDev, int onoff ) { switch ( onoff ) { case DEVICE_INIT: XDirectFBKeyboardInit( pDev ); break; case DEVICE_ON: pDev->public.on = TRUE; AddEnabledDevice (directfbEventsPipe[0]); break; case DEVICE_OFF: RemoveEnabledDevice (directfbEventsPipe[0]); pDev->public.on = FALSE; break; case DEVICE_CLOSE: break; } return Success; } static void HandleWindowEvent (DFBWindowEvent *ev) { xEvent xe; /* returns TRUE if event is no input related event */ if (XDirectFBProcessWindowEvent (ev)) return; /* translate it to an X event and post it */ memset(&xe, 0, sizeof(xe)); /* Shift from global screen coordinates to coordinates relative to the origin of the current screen. */ xe.u.keyButtonPointer.rootX = ev->cx - directfbMainScreenX - dixScreenOrigins[miPointerCurrentScreen()->myNum].x; xe.u.keyButtonPointer.rootY = ev->cy - directfbMainScreenY - dixScreenOrigins[miPointerCurrentScreen()->myNum].y; xe.u.keyButtonPointer.time = GetTimeInMillis(); switch (ev->type) { case DWET_ENTER: case DWET_MOTION: xe.u.u.type = MotionNotify; (directfbPointer->public.processInputProc) ( &xe, directfbPointer, 1 ); miPointerAbsoluteCursor (ev->cx, ev->cy, GetTimeInMillis()); break; case DWET_KEYUP: case DWET_KEYDOWN: switch (DFB_KEY_TYPE (ev->key_symbol)) { case DIKT_MODIFIER: case DIKT_LOCK: return; default: break; } xe.u.u.type = (ev->type == DWET_KEYUP) ? KeyRelease : KeyPress; if (ev->key_code >= directfbMinScanCode && ev->key_code <= directfbMaxScanCode) { xe.u.u.detail = ev->key_code + DIRECTFB_MIN_KEYCODE - directfbMinScanCode; } else return; (directfbKeyboard->public.processInputProc)(&(xe), directfbKeyboard, 1); break; case DWET_WHEEL: { int count = ev->step; if (count > 0) { xe.u.u.detail = SCROLLWHEELUPFAKE; } else { xe.u.u.detail = SCROLLWHEELDOWNFAKE; count = -count; } for (; count; --count) { xe.u.u.type = ButtonPress; (directfbPointer->public.processInputProc) ( &xe, directfbPointer, 1 ); xe.u.u.type = ButtonRelease; (directfbPointer->public.processInputProc) ( &xe, directfbPointer, 1 ); } break; } case DWET_BUTTONUP: case DWET_BUTTONDOWN: xe.u.u.type = (ev->type == DWET_BUTTONUP) ? ButtonRelease : ButtonPress; switch (ev->button) { case DIBI_LEFT: xe.u.u.detail = 1; break; case DIBI_MIDDLE: xe.u.u.detail = 2; break; case DIBI_RIGHT: xe.u.u.detail = 3; break; default: return; } (directfbPointer->public.processInputProc) ( &xe, directfbPointer, 1 ); break; default: ; } } static void HandleInputEvent (DFBInputEvent *ev) { xEvent xe; switch (ev->type) { case DIET_KEYPRESS: if (ev->key_id == DIKI_BACKSPACE && (ev->modifiers & (DIMM_CONTROL | DIMM_ALT)) == (DIMM_CONTROL | DIMM_ALT)) GiveUp(0); /* fall through */ case DIET_KEYRELEASE: XDirectFBDGADispatchKey( screenInfo.screens[0], ev, directfbKeyboard ); switch (DFB_KEY_TYPE (ev->key_symbol)) { case DIKT_MODIFIER: case DIKT_LOCK: break; default: return; } /* translate it to an X event and post it */ memset(&xe, 0, sizeof(xe)); /* Shift from global screen coordinates to coordinates relative to the origin of the current screen. */ xe.u.keyButtonPointer.rootX = 0; /* FIXME */ xe.u.keyButtonPointer.rootY = 0; xe.u.keyButtonPointer.time = GetTimeInMillis(); xe.u.u.type = (ev->type == DIET_KEYRELEASE) ? KeyRelease : KeyPress; if (ev->key_code >= directfbMinScanCode && ev->key_code <= directfbMaxScanCode) { xe.u.u.detail = ev->key_code + DIRECTFB_MIN_KEYCODE - directfbMinScanCode; } else return; (directfbKeyboard->public.processInputProc)(&(xe), directfbKeyboard, 1); break; case DIET_AXISMOTION: XDirectFBDGADispatchMotion( screenInfo.screens[0], ev, directfbPointer ); break; case DIET_BUTTONPRESS: case DIET_BUTTONRELEASE: XDirectFBDGADispatchButton( screenInfo.screens[0], ev, directfbPointer ); break; default: break; } } /* =========================================================================== Functions needed to link against device independent X =========================================================================== */ /* * ProcessInputEvents * Read and process events from the event pipe until it is empty. */ void ProcessInputEvents(void) { DFBEvent dfbevent; if (directfbEventsPipe[0] == -1) return; while (read (directfbEventsPipe[0], &dfbevent, sizeof(DFBEvent)) == sizeof(DFBEvent)) { switch (dfbevent.clazz) { case DFEC_WINDOW: HandleWindowEvent (&dfbevent.window); break; case DFEC_INPUT: HandleInputEvent (&dfbevent.input); break; default: ; } } miPointerUpdate(); } /* * InitInput * Register the keyboard and mouse devices */ void InitInput (int argc, char **argv) { directfbPointer = AddInputDevice(XDirectFBMouseProc, TRUE); RegisterPointerDevice( directfbPointer ); directfbKeyboard = AddInputDevice(XDirectFBKeybdProc, TRUE); RegisterKeyboardDevice( directfbKeyboard ); } /* * InitOutput * Initialize screenInfo for all actually accessible framebuffers. * * The display mode dependent code gets called three times. The mode * specific InitOutput routines are expected to discover the number * of potentially useful screens and cache routes to them internally. * Inside DirectFBAddScreen are two other mode specific calls. * A mode specific AddScreen routine is called for each screen to * actually initialize the screen with the ScreenPtr structure. * After other screen setup has been done, a mode specific * SetupScreen function can be called to finalize screen setup. */ void InitOutput (ScreenInfo *pScreenInfo, int argc, char **argv) { int i, left, top; pScreenInfo->imageByteOrder = IMAGE_BYTE_ORDER; pScreenInfo->bitmapScanlineUnit = BITMAP_SCANLINE_UNIT; pScreenInfo->bitmapScanlinePad = BITMAP_SCANLINE_PAD; pScreenInfo->bitmapBitOrder = BITMAP_BIT_ORDER; /* List how we want common pixmap formats to be padded */ pScreenInfo->numPixmapFormats = NUMFORMATS; for (i = 0; i < NUMFORMATS; i++) pScreenInfo->formats[i] = formats[i]; if (serverGeneration == 1) { /* Create a pipe for events */ if (pipe (directfbEventsPipe) < 0) { perror (__FUNCTION__": pipe"); return; } if (fcntl (directfbEventsPipe[0], F_SETFL, O_NONBLOCK) < 0) { perror (__FUNCTION__": fcntl"); return; } } /* Discover screens and do mode specific initialization */ if (XDirectFBInitOutput(argc, argv)) AddScreen( XDirectFBAddScreen, argc, argv ); /* Shift all screens so the X11 (0, 0) coordinate is at the top left of the global screen coordinates. Screens can be arranged so the top left isnt on any screen, so instead use the top left of the leftmost screen as (0,0). This may mean some screen space is in -y, but its better that (0,0) be onscreen, or else default xterms disappear. Its better that -y be used than -x, because when popup menus are forced "onscreen" by dumb window managers like twm, theyll shift the menus down instead of left, which still looks funny but is an easier target to hit. */ left = dixScreenOrigins[0].x; top = dixScreenOrigins[0].y; /* Find leftmost screen. If theres a tie, take the topmost of the two. */ for (i = 1; i < pScreenInfo->numScreens; i++) { if (dixScreenOrigins[i].x < left || (dixScreenOrigins[i].x == left && dixScreenOrigins[i].y < top)) { left = dixScreenOrigins[i].x; top = dixScreenOrigins[i].y; } } directfbMainScreenX = left; directfbMainScreenY = top; /* Shift all screens so that there is a screen whose top left is at X11 (0,0) and at global screen coordinate (directfbMainScreenX, directfbMainScreenY). */ if (directfbMainScreenX != 0 || directfbMainScreenY != 0) { for (i = 0; i < pScreenInfo->numScreens; i++) { dixScreenOrigins[i].x -= directfbMainScreenX; dixScreenOrigins[i].y -= directfbMainScreenY; ErrorF("Screen %d placed at X11 coordinate (%d,%d).\n", i, dixScreenOrigins[i].x, dixScreenOrigins[i].y); } } } /* * OsVendorFataError */ void OsVendorFatalError (void) { ErrorF( " OsVendorFatalError\n" ); } /* * OsVendorInit * Initialization of DirectFB support. */ void OsVendorInit (void) { if (serverGeneration == 1) { XDirectFBPrintBanner(); } } /* * ddxProcessArgument -- * Process device-dependent command line args. Returns 0 if argument is * not device dependent, otherwise Count of number of elements of argv * that are part of a device dependent commandline option. */ int ddxProcessArgument( int argc, char *argv[], int i ) { if ( !strncmp( argv[i], "--dfb:", 6 ) ) return 1; if ( !strcmp( argv[i], "-defaultOpacity" ) ) { if ( i == argc-1 ) FatalError( "-defaultOpacity must be followed by a number\n" ); directfbPrefs.defaultOpacity = atoi( argv[i+1] ); if (directfbPrefs.defaultOpacity < 1 || directfbPrefs.defaultOpacity > 255) FatalError( "Opacity ranges from 1 to 255.\n" ); return 2; } if ( !strcmp( argv[i], "-unfocusedOpacity" ) ) { if ( i == argc-1 ) FatalError( "-unfocusedOpacity must be followed by a number\n" ); directfbPrefs.unfocusedOpacity = atoi( argv[i+1] ); if (directfbPrefs.unfocusedOpacity < 1 || directfbPrefs.unfocusedOpacity > 255) FatalError( "Opacity ranges from 1 to 255.\n" ); return 2; } if ( !strcmp( argv[i], "-enableUnfocused" ) ) { directfbPrefs.enableUnfocused = TRUE; return 1; } if ( !strcmp( argv[i], "-enableFadeIn" ) ) { directfbPrefs.enableFadeIn = TRUE; return 1; } if ( !strcmp( argv[i], "-enableFadeOut" ) ) { directfbPrefs.enableFadeOut = TRUE; return 1; } if ( !strcmp( argv[i], "-enableRoot" ) ) { directfbPrefs.enableRoot = TRUE; return 1; } if ( !strcmp( argv[i], "-displayLayer" ) ) { if ( i == argc-1 ) FatalError( "-displayLayer must be followed by an ID (0=primary)\n" ); directfbPrefs.layerID = atoi( argv[i+1] ); if (directfbPrefs.layerID < 0) FatalError( "Layer ID must be >= 0.\n" ); return 2; } if (!strcmp( argv[i], "-showconfig" ) || !strcmp( argv[i], "-version" )) { XDirectFBPrintBanner(); exit(0); } return 0; } /* * ddxUseMsg -- * Print out correct use of device dependent commandline options. * Maybe the user now knows what really to do ... */ void ddxUseMsg( void ) { ErrorF("\n"); ErrorF("\n"); ErrorF("Device Dependent Usage:\n"); ErrorF("\n"); ErrorF("-defaultOpacity <1-255> : default opacity for created top level windows.\n"); ErrorF("-unfocusedOpacity <1-255> : opacity for unfocused top level windows.\n"); ErrorF("\n"); ErrorF("-enableUnfocused : enable usage of unfocused opacity value.\n"); ErrorF("-enableFadeIn : enable fade in of showing windows.\n"); ErrorF("-enableFadeOut : enable fade out of hiding windows.\n"); ErrorF("-enableRoot : enable creation of a root window.\n"); ErrorF("-displayLayer : choose display layer (0=primary, \n"); ErrorF(" for other IDs try \"dfbinfo\").\n"); ErrorF("\n"); } /* * ddxGiveUp -- * Device dependent cleanup. Called by dix before normal server death. */ void ddxGiveUp( void ) { ErrorF( "Quitting XDirectFB...\n" ); XDirectFBCloseOutput(); } /* * AbortDDX -- * DDX - specific abort routine. Called by AbortServer(). The attempt is * made to restore all original setting of the displays. Also all devices * are closed. */ void AbortDDX( void ) { ErrorF( " AbortDDX\n" ); /* * This is needed for a abnormal server exit, since the normal exit stuff * MUST also be performed (i.e. the vt must be left in a defined state) */ ddxGiveUp(); } #ifdef DPMSExtension /* * DPMS extension */ Bool DPMSSupported(void) { return TRUE; } void DPMSSet(int level) { IDirectFBDisplayLayer *layer; DFBScreenPowerMode mode; IDirectFBScreen *screen; if (!dfb) return; switch (level) { case DPMSModeOff: mode = DSPM_OFF; break; case DPMSModeSuspend: mode = DSPM_SUSPEND; break; case DPMSModeStandby: mode = DSPM_STANDBY; break; case DPMSModeOn: mode = DSPM_ON; break; default: return; } if (dfb->GetDisplayLayer (dfb, directfbPrefs.layerID, &layer)) return; if (layer->SetCooperativeLevel (layer, DLSCL_ADMINISTRATIVE) == DFB_OK) { layer->GetScreen(layer, &screen); if (screen->SetPowerMode (screen, mode) == DFB_OK) DPMSPowerLevel = level; } layer->Release (layer); } int DPMSGet(int *level) { return DPMSPowerLevel; } #endif CARD32 GetTimeInMillis(void) { struct timeval tp; X_GETTIMEOFDAY(&tp); return(tp.tv_sec * 1000) + (tp.tv_usec / 1000); } #include "mivalidate.h" /* for union _Validate used by windowstr.h */ #include "windowstr.h" /* for struct _Window */ #include "scrnintstr.h" /* for struct _Screen */ /* This is copied from Xserver/hw/xfree86/common/xf86Helper.c. DirectFB mode uses this when switching in and out of DirectFB. DirectFB or IOKit can use this when waking from sleep. Copyright (c) 1997-1998 by The XFree86 Project, Inc. */ /* * xf86SetRootClip -- * Enable or disable rendering to the screen by * setting the root clip list and revalidating * all of the windows */ void xf86SetRootClip (ScreenPtr pScreen, BOOL enable) { WindowPtr pWin = WindowTable[pScreen->myNum]; WindowPtr pChild; Bool WasViewable = (Bool)(pWin->viewable); Bool anyMarked = TRUE; RegionPtr pOldClip = NULL, bsExposed; #ifdef DO_SAVE_UNDERS Bool dosave = FALSE; #endif WindowPtr pLayerWin; BoxRec box; if (WasViewable) { for (pChild = pWin->firstChild; pChild; pChild = pChild->nextSib) { (void) (*pScreen->MarkOverlappedWindows)(pChild, pChild, &pLayerWin); } (*pScreen->MarkWindow) (pWin); anyMarked = TRUE; if (pWin->valdata) { if (HasBorder (pWin)) { RegionPtr borderVisible; borderVisible = REGION_CREATE(pScreen, NullBox, 1); REGION_SUBTRACT(pScreen, borderVisible, &pWin->borderClip, &pWin->winSize); pWin->valdata->before.borderVisible = borderVisible; } pWin->valdata->before.resized = TRUE; } } /* * Use REGION_BREAK to avoid optimizations in ValidateTree * that assume the root borderClip can't change well, normally * it doesn't...) */ if (enable) { box.x1 = 0; box.y1 = 0; box.x2 = pScreen->width; box.y2 = pScreen->height; REGION_RESET(pScreen, &pWin->borderClip, &box); REGION_BREAK (pWin->drawable.pScreen, &pWin->clipList); } else { REGION_EMPTY(pScreen, &pWin->borderClip); REGION_BREAK (pWin->drawable.pScreen, &pWin->clipList); } ResizeChildrenWinSize (pWin, 0, 0, 0, 0); if (WasViewable) { if (pWin->backStorage) { pOldClip = REGION_CREATE(pScreen, NullBox, 1); REGION_COPY(pScreen, pOldClip, &pWin->clipList); } if (pWin->firstChild) { anyMarked |= (*pScreen->MarkOverlappedWindows)(pWin->firstChild, pWin->firstChild, (WindowPtr *)NULL); } else { (*pScreen->MarkWindow) (pWin); anyMarked = TRUE; } #ifdef DO_SAVE_UNDERS if (DO_SAVE_UNDERS(pWin)) { dosave = (*pScreen->ChangeSaveUnder)(pLayerWin, pLayerWin); } #endif /* DO_SAVE_UNDERS */ if (anyMarked) (*pScreen->ValidateTree)(pWin, NullWindow, VTOther); } if (pWin->backStorage && ((pWin->backingStore == Always) || WasViewable)) { if (!WasViewable) pOldClip = &pWin->clipList; /* a convenient empty region */ bsExposed = (*pScreen->TranslateBackingStore) (pWin, 0, 0, pOldClip, pWin->drawable.x, pWin->drawable.y); if (WasViewable) REGION_DESTROY(pScreen, pOldClip); if (bsExposed) { RegionPtr valExposed = NullRegion; if (pWin->valdata) valExposed = &pWin->valdata->after.exposed; (*pScreen->WindowExposures) (pWin, valExposed, bsExposed); if (valExposed) REGION_EMPTY(pScreen, valExposed); REGION_DESTROY(pScreen, bsExposed); } } if (WasViewable) { if (anyMarked) (*pScreen->HandleExposures)(pWin); #ifdef DO_SAVE_UNDERS if (dosave) (*pScreen->PostChangeSaveUnder)(pLayerWin, pLayerWin); #endif /* DO_SAVE_UNDERS */ if (anyMarked && pScreen->PostValidateTree) (*pScreen->PostValidateTree)(pWin, NullWindow, VTOther); } if (pWin->realized) WindowsRestructured (); FlushAllOutput (); }