// Copyright (C) 2002-2007 Nikolaus Gebhardt // Copyright (C) 2007-2012 Christian Stehno // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h #include "CIrrDeviceFB.h" #ifdef _IRR_COMPILE_WITH_FB_DEVICE_ #include #include #include #include #include #include #include #include #include #include #include #include "IEventReceiver.h" #include "os.h" #include "CTimer.h" #include "irrString.h" #include "Keycodes.h" #include "COSOperator.h" #include "CColorConverter.h" #include "SIrrCreationParameters.h" #include "CEGLManager.h" #include namespace irr { namespace video { #ifdef _IRR_COMPILE_WITH_OGLES1_ IVideoDriver* createOGLES1Driver(const SIrrlichtCreationParameters& params, io::IFileSystem* io, video::IContextManager* contextManager); #endif #ifdef _IRR_COMPILE_WITH_OGLES2_ IVideoDriver* createOGLES2Driver(const SIrrlichtCreationParameters& params, io::IFileSystem* io, video::IContextManager* contextManager); #endif } } namespace irr { //! constructor CIrrDeviceFB::CIrrDeviceFB(const SIrrlichtCreationParameters& params) : CIrrDeviceStub(params), Framebuffer(-1), EventDevice(-1), SoftwareImage(0), Pitch(0), FBColorFormat(video::ECF_A8R8G8B8), Close(false) { #ifdef _DEBUG setDebugName("CIrrDeviceFB"); #endif // print version, distribution etc. // thx to LynxLuna for pointing me to the uname function core::stringc linuxversion; struct utsname FBInfo; uname(&FBInfo); linuxversion += FBInfo.sysname; linuxversion += " "; linuxversion += FBInfo.release; linuxversion += " "; linuxversion += FBInfo.version; linuxversion += " "; linuxversion += FBInfo.machine; Operator = new COSOperator(linuxversion); os::Printer::log(linuxversion.c_str(), ELL_INFORMATION); // create window if (params.DriverType != video::EDT_NULL) { // create the window, only if we do not use the null device if (!createWindow(params.WindowSize, params.Bits)) return; } // create cursor control CursorControl = new CCursorControl(this, params.DriverType == video::EDT_NULL); // create driver createDriver(); if (!VideoDriver) return; createGUIAndScene(); } //! destructor CIrrDeviceFB::~CIrrDeviceFB() { if (SoftwareImage) munmap(SoftwareImage, CreationParams.WindowSize.Height*Pitch); // go back to previous format if (ioctl(Framebuffer, FBIOPUT_VSCREENINFO, &oldscreeninfo) <0) perror("Restoring old fb mode"); if (KeyboardDevice != -1) if (ioctl(KeyboardDevice, KDSETMODE, &KeyboardMode) <0) perror("Restoring keyboard mode"); if (EventDevice != -1) close(EventDevice); if (KeyboardDevice != -1) close(KeyboardDevice); if (Framebuffer != -1) close(Framebuffer); } bool CIrrDeviceFB::createWindow(const core::dimension2d& windowSize, u32 bits) { char buf[256]; CreationParams.WindowSize.Width = windowSize.Width; CreationParams.WindowSize.Height = windowSize.Height; KeyboardDevice = open("/dev/tty", O_RDWR); if (KeyboardDevice == -1) perror("Open keyboard"); if (ioctl(KeyboardDevice, KDGETMODE, &KeyboardMode) <0) perror("Read keyboard mode"); if (ioctl(KeyboardDevice, KDSETMODE, KD_GRAPHICS) <0) perror("Set keyboard mode"); Framebuffer=open("/dev/fb/0", O_RDWR); if (Framebuffer == -1) { Framebuffer=open("/dev/fb0", O_RDWR); if (Framebuffer == -1) { perror("Open framebuffer"); return false; } } EventDevice = open("/dev/input/event0", O_RDONLY | O_NONBLOCK); if (EventDevice == -1) perror("Open event device"); // make format settings ioctl(Framebuffer, FBIOGET_FSCREENINFO, &fbfixscreeninfo); ioctl(Framebuffer, FBIOGET_VSCREENINFO, &oldscreeninfo); snprintf_irr(buf, 256, "Original resolution: %d x %d\nARGB%d%d%d%d\n",oldscreeninfo.xres,oldscreeninfo.yres, oldscreeninfo.transp.length,oldscreeninfo.red.length,oldscreeninfo.green.length,oldscreeninfo.blue.length); os::Printer::log(buf); memcpy(&fbscreeninfo, &oldscreeninfo, sizeof(struct fb_var_screeninfo)); if (CreationParams.DriverType != video::EDT_NULL) { fbscreeninfo.xres = fbscreeninfo.xres_virtual = CreationParams.WindowSize.Width; fbscreeninfo.yres = fbscreeninfo.yres_virtual = CreationParams.WindowSize.Height; fbscreeninfo.bits_per_pixel = 16; fbscreeninfo.red.offset = 10; fbscreeninfo.red.length = 5; fbscreeninfo.green.offset = 5; fbscreeninfo.green.length = 5; fbscreeninfo.blue.offset = 0; fbscreeninfo.blue.length = 5; fbscreeninfo.transp.offset = 15; fbscreeninfo.transp.length = 1; ioctl(Framebuffer, FBIOPUT_VSCREENINFO, &fbscreeninfo); ioctl(Framebuffer, FBIOGET_VSCREENINFO, &fbscreeninfo); snprintf_irr(buf, 256, "New resolution: %d x %d (%d x %d)\nARGB%d%d%d%d\n",fbscreeninfo.xres,fbscreeninfo.yres,fbscreeninfo.xres_virtual,fbscreeninfo.yres_virtual, fbscreeninfo.transp.length,fbscreeninfo.red.length,fbscreeninfo.green.length,fbscreeninfo.blue.length); os::Printer::log(buf); CreationParams.WindowSize.Width = fbscreeninfo.xres; CreationParams.WindowSize.Height = fbscreeninfo.yres; CreationParams.Bits = fbscreeninfo.bits_per_pixel; Pitch = fbfixscreeninfo.line_length; if (fbscreeninfo.bits_per_pixel == 16) { if (fbscreeninfo.transp.length == 0) FBColorFormat = video::ECF_R5G6B5; else FBColorFormat = video::ECF_A1R5G5B5; } else { if (fbscreeninfo.transp.length == 0) FBColorFormat = video::ECF_R8G8B8; else FBColorFormat = video::ECF_A8R8G8B8; } if (MAP_FAILED==(SoftwareImage=(u8*)mmap(0, CreationParams.WindowSize.Height*Pitch, PROT_READ|PROT_WRITE, MAP_SHARED, Framebuffer, 0))) { perror("mmap render target"); return false; } } return true; } //! create the driver void CIrrDeviceFB::createDriver() { switch(CreationParams.DriverType) { case video::EDT_SOFTWARE: #ifdef _IRR_COMPILE_WITH_SOFTWARE_ VideoDriver = video::createSoftwareDriver(CreationParams.WindowSize, CreationParams.Fullscreen, FileSystem, this); #else os::Printer::log("No Software driver support compiled in.", ELL_WARNING); #endif break; case video::EDT_BURNINGSVIDEO: #ifdef _IRR_COMPILE_WITH_BURNINGSVIDEO_ VideoDriver = video::createBurningVideoDriver(CreationParams, FileSystem, this); #else os::Printer::log("Burning's video driver was not compiled in.", ELL_WARNING); #endif break; case video::EDT_OGLES2: #ifdef _IRR_COMPILE_WITH_OGLES2_ { video::SExposedVideoData data; s32 width = 0; s32 height = 0; NativeDisplayType display = fbGetDisplay(0); fbGetDisplayGeometry(display, &width, &height); data.OpenGLFB.Window = (void*)fbCreateWindow(display, 0, 0, width, height); ContextManager = new video::CEGLManager(); ContextManager->initialize(CreationParams, data); VideoDriver = video::createOGLES2Driver(CreationParams, FileSystem, ContextManager); } #else os::Printer::log("No OpenGL-ES2 support compiled in.", ELL_ERROR); #endif break; case video::EDT_OGLES1: #ifdef _IRR_COMPILE_WITH_OGLES1_ { video::SExposedVideoData data; s32 width = 0; s32 height = 0; NativeDisplayType display = fbGetDisplay(0); fbGetDisplayGeometry(display, &width, &height); data.OpenGLFB.Window = (void*)fbCreateWindow(display, 0, 0, width, height); ContextManager = new video::CEGLManager(); ContextManager->initialize(CreationParams, data); VideoDriver = video::createOGLES1Driver(CreationParams, FileSystem, ContextManager); } #else os::Printer::log("No OpenGL-ES1 support compiled in.", ELL_ERROR); #endif break; case video::DEPRECATED_EDT_DIRECT3D8_NO_LONGER_EXISTS: case video::EDT_OPENGL: case video::EDT_DIRECT3D9: os::Printer::log("This driver is not available in FB. Try Software renderer.", ELL_WARNING); break; default: VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize); break; } } //! runs the device. Returns false if device wants to be deleted bool CIrrDeviceFB::run() { os::Timer::tick(); struct input_event ev; if (EventDevice>=0) { if ((read(EventDevice, &ev, sizeof(input_event)) < 0) && errno != EAGAIN) perror("Read input event"); if (ev.type == EV_KEY) { irr::SEvent irrevent; irrevent.EventType = irr::EET_KEY_INPUT_EVENT; irrevent.KeyInput.PressedDown = (ev.value == 1); switch (ev.code) { case KEY_RIGHTCTRL: case KEY_LEFTCTRL: irrevent.KeyInput.Control = true; break; case KEY_RIGHTSHIFT: case KEY_LEFTSHIFT: irrevent.KeyInput.Shift = true; break; case KEY_ESC: irrevent.KeyInput.Key = (EKEY_CODE)0x1B; break; case KEY_SPACE: irrevent.KeyInput.Key = (EKEY_CODE)0x20; break; case KEY_UP: irrevent.KeyInput.Key = (EKEY_CODE)0x26; break; case KEY_LEFT: irrevent.KeyInput.Key = (EKEY_CODE)0x25; break; case KEY_RIGHT: irrevent.KeyInput.Key = (EKEY_CODE)0x27; break; case KEY_DOWN: irrevent.KeyInput.Key = (EKEY_CODE)0x28; break; case KEY_A: irrevent.KeyInput.Key = (EKEY_CODE)0x41; break; case KEY_B: irrevent.KeyInput.Key = (EKEY_CODE)0x42; break; case KEY_C: irrevent.KeyInput.Key = (EKEY_CODE)0x43; break; case KEY_D: irrevent.KeyInput.Key = (EKEY_CODE)0x44; break; case KEY_E: irrevent.KeyInput.Key = (EKEY_CODE)0x45; break; case KEY_F: irrevent.KeyInput.Key = (EKEY_CODE)0x46; break; case KEY_G: irrevent.KeyInput.Key = (EKEY_CODE)0x47; break; case KEY_H: irrevent.KeyInput.Key = (EKEY_CODE)0x48; break; case KEY_I: irrevent.KeyInput.Key = (EKEY_CODE)0x49; break; case KEY_J: irrevent.KeyInput.Key = (EKEY_CODE)0x4A; break; case KEY_K: irrevent.KeyInput.Key = (EKEY_CODE)0x4B; break; case KEY_L: irrevent.KeyInput.Key = (EKEY_CODE)0x4C; break; case KEY_M: irrevent.KeyInput.Key = (EKEY_CODE)0x4D; break; case KEY_N: irrevent.KeyInput.Key = (EKEY_CODE)0x4E; break; case KEY_O: irrevent.KeyInput.Key = (EKEY_CODE)0x4F; break; case KEY_P: irrevent.KeyInput.Key = (EKEY_CODE)0x50; break; case KEY_Q: irrevent.KeyInput.Key = (EKEY_CODE)0x51; break; case KEY_R: irrevent.KeyInput.Key = (EKEY_CODE)0x52; break; case KEY_S: irrevent.KeyInput.Key = (EKEY_CODE)0x53; break; case KEY_T: irrevent.KeyInput.Key = (EKEY_CODE)0x54; break; case KEY_U: irrevent.KeyInput.Key = (EKEY_CODE)0x55; break; case KEY_V: irrevent.KeyInput.Key = (EKEY_CODE)0x56; break; case KEY_W: irrevent.KeyInput.Key = (EKEY_CODE)0x57; break; case KEY_X: irrevent.KeyInput.Key = (EKEY_CODE)0x58; break; case KEY_Y: irrevent.KeyInput.Key = (EKEY_CODE)0x59; break; case KEY_Z: irrevent.KeyInput.Key = (EKEY_CODE)0x5A; break; default: irrevent.KeyInput.Key = (EKEY_CODE)0; break; } postEventFromUser(irrevent); } } return !Close; } //! Pause the current process for the minimum time allowed only to allow other processes to execute void CIrrDeviceFB::yield() { struct timespec ts = {0,0}; nanosleep(&ts, NULL); } //! Pause execution and let other processes to run for a specified amount of time. void CIrrDeviceFB::sleep(u32 timeMs, bool pauseTimer=false) { bool wasStopped = Timer ? Timer->isStopped() : true; struct timespec ts; ts.tv_sec = (time_t) (timeMs / 1000); ts.tv_nsec = (long) (timeMs % 1000) * 1000000; if (pauseTimer && !wasStopped) Timer->stop(); nanosleep(&ts, NULL); if (pauseTimer && !wasStopped) Timer->start(); } //! presents a surface in the client area bool CIrrDeviceFB::present(video::IImage* image, void* windowId, core::rect* src ) { // this is only necessary for software drivers. if (CreationParams.DriverType != video::EDT_SOFTWARE && CreationParams.DriverType != video::EDT_BURNINGSVIDEO) return false; if (!SoftwareImage) return false; u8* destData = SoftwareImage; u32 srcwidth = (u32)image->getDimension().Width; u32 srcheight = (u32)image->getDimension().Height; // clip images srcheight = core::min_(srcheight, CreationParams.WindowSize.Height); srcwidth = core::min_(srcwidth, CreationParams.WindowSize.Width); u8* srcdata = (u8*)image->lock(); for (u32 y=0; ygetColorFormat(), srcwidth, destData, FBColorFormat); srcdata+=image->getPitch(); destData+=Pitch; } image->unlock(); msync(SoftwareImage,CreationParams.WindowSize.Width*CreationParams.WindowSize.Height,MS_ASYNC); return true; } //! notifies the device that it should close itself void CIrrDeviceFB::closeDevice() { Close = true; } //! returns if window is active. if not, nothing need to be drawn bool CIrrDeviceFB::isWindowActive() const { return true; } //! returns if window has focus bool CIrrDeviceFB::isWindowFocused() const { return true; } //! returns if window is minimized bool CIrrDeviceFB::isWindowMinimized() const { return false; } //! sets the caption of the window void CIrrDeviceFB::setWindowCaption(const wchar_t* text) { } //! Sets if the window should be resizeable in windowed mode. void CIrrDeviceFB::setResizable(bool resize) { } //! Minimizes window void CIrrDeviceFB::minimizeWindow() { } //! Maximizes window void CIrrDeviceFB::maximizeWindow() { } //! Restores original window size void CIrrDeviceFB::restoreWindow() { } //! Returns the type of this device E_DEVICE_TYPE CIrrDeviceFB::getType() const { return EIDT_FRAMEBUFFER; } } // end namespace irr #endif // _IRR_USE_FB_DEVICE_