CIrrDeviceConsole.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. // Copyright (C) 2009-2012 Gaz Davidson
  2. // This file is part of the "Irrlicht Engine".
  3. // For conditions of distribution and use, see copyright notice in irrlicht.h
  4. #include "CIrrDeviceConsole.h"
  5. #ifdef _IRR_COMPILE_WITH_CONSOLE_DEVICE_
  6. #include "os.h"
  7. #include "IGUISkin.h"
  8. #include "IGUIEnvironment.h"
  9. // to close the device on terminate signal
  10. irr::CIrrDeviceConsole *DeviceToClose;
  11. #ifdef _IRR_WINDOWS_NT_CONSOLE_
  12. // Callback for Windows
  13. BOOL WINAPI ConsoleHandler(DWORD CEvent)
  14. {
  15. switch(CEvent)
  16. {
  17. case CTRL_C_EVENT:
  18. irr::os::Printer::log("Closing console device", "CTRL+C");
  19. break;
  20. case CTRL_BREAK_EVENT:
  21. irr::os::Printer::log("Closing console device", "CTRL+Break");
  22. break;
  23. case CTRL_CLOSE_EVENT:
  24. irr::os::Printer::log("Closing console device", "User closed console");
  25. break;
  26. case CTRL_LOGOFF_EVENT:
  27. irr::os::Printer::log("Closing console device", "User is logging off");
  28. break;
  29. case CTRL_SHUTDOWN_EVENT:
  30. irr::os::Printer::log("Closing console device", "Computer shutting down");
  31. break;
  32. }
  33. DeviceToClose->closeDevice();
  34. return TRUE;
  35. }
  36. #elif defined(_IRR_POSIX_API_)
  37. // sigterm handler
  38. #include <signal.h>
  39. void sighandler(int sig)
  40. {
  41. irr::core::stringc code = "Signal ";
  42. code += sig;
  43. code += " received";
  44. irr::os::Printer::log("Closing console device", code.c_str());
  45. DeviceToClose->closeDevice();
  46. }
  47. #endif
  48. namespace irr
  49. {
  50. const c8 ASCIIArtChars[] = " .,'~:;!+>=icopjtJY56SB8XDQKHNWM"; //MWNHKQDX8BS65YJtjpoci=+>!;:~',. ";
  51. const u16 ASCIIArtCharsCount = 32;
  52. //const c8 ASCIIArtChars[] = " \xb0\xb1\xf9\xb2\xdb";
  53. //const u16 ASCIIArtCharsCount = 5;
  54. //! constructor
  55. CIrrDeviceConsole::CIrrDeviceConsole(const SIrrlichtCreationParameters& params)
  56. : CIrrDeviceStub(params), IsWindowFocused(true), ConsoleFont(0), OutFile(stdout)
  57. {
  58. DeviceToClose = this;
  59. #ifdef _IRR_WINDOWS_NT_CONSOLE_
  60. MouseButtonStates = 0;
  61. WindowsSTDIn = GetStdHandle(STD_INPUT_HANDLE);
  62. WindowsSTDOut = GetStdHandle(STD_OUTPUT_HANDLE);
  63. PCOORD Dimensions = 0;
  64. if (CreationParams.Fullscreen)
  65. {
  66. // Some mingw versions lack this define, so avoid it in case it does not exist
  67. #if (_WIN32_WINNT >= 0x0501) && defined(CONSOLE_FULLSCREEN_MODE)
  68. if (SetConsoleDisplayMode(WindowsSTDOut, CONSOLE_FULLSCREEN_MODE, Dimensions))
  69. {
  70. CreationParams.WindowSize.Width = Dimensions->X;
  71. CreationParams.WindowSize.Width = Dimensions->Y;
  72. }
  73. #endif
  74. }
  75. else
  76. {
  77. COORD ConsoleSize;
  78. ConsoleSize.X = CreationParams.WindowSize.Width;
  79. ConsoleSize.X = CreationParams.WindowSize.Height;
  80. SetConsoleScreenBufferSize(WindowsSTDOut, ConsoleSize);
  81. }
  82. // catch windows close/break signals
  83. SetConsoleCtrlHandler((PHANDLER_ROUTINE)ConsoleHandler, TRUE);
  84. #elif defined(_IRR_POSIX_API_)
  85. // catch other signals
  86. signal(SIGABRT, &sighandler);
  87. signal(SIGTERM, &sighandler);
  88. signal(SIGINT, &sighandler);
  89. // set output stream
  90. if (params.WindowId)
  91. OutFile = (FILE*)(params.WindowId);
  92. #endif
  93. #ifdef _IRR_VT100_CONSOLE_
  94. // reset terminal
  95. fprintf(OutFile, "%cc", 27);
  96. // disable line wrapping
  97. fprintf(OutFile, "%c[7l", 27);
  98. #endif
  99. switch (params.DriverType)
  100. {
  101. case video::EDT_SOFTWARE:
  102. #ifdef _IRR_COMPILE_WITH_SOFTWARE_
  103. VideoDriver = video::createSoftwareDriver(CreationParams.WindowSize, CreationParams.Fullscreen, FileSystem, this);
  104. #else
  105. os::Printer::log("Software driver was not compiled in.", ELL_ERROR);
  106. #endif
  107. break;
  108. case video::EDT_BURNINGSVIDEO:
  109. #ifdef _IRR_COMPILE_WITH_BURNINGSVIDEO_
  110. VideoDriver = video::createBurningVideoDriver(CreationParams, FileSystem, this);
  111. #else
  112. os::Printer::log("Burning's Video driver was not compiled in.", ELL_ERROR);
  113. #endif
  114. break;
  115. case video::EDT_DIRECT3D8:
  116. case video::EDT_DIRECT3D9:
  117. case video::EDT_OPENGL:
  118. os::Printer::log("The console device cannot use hardware drivers yet.", ELL_ERROR);
  119. break;
  120. case video::EDT_NULL:
  121. VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize);
  122. break;
  123. default:
  124. break;
  125. }
  126. // set up output buffer
  127. for (u32 y=0; y<CreationParams.WindowSize.Height; ++y)
  128. {
  129. core::stringc str;
  130. str.reserve(CreationParams.WindowSize.Width);
  131. for (u32 x=0; x<CreationParams.WindowSize.Width; ++x)
  132. str += " ";
  133. OutputBuffer.push_back(str);
  134. }
  135. #ifdef _IRR_WINDOWS_NT_CONSOLE_
  136. CursorControl = new CCursorControl(CreationParams.WindowSize);
  137. #endif
  138. if (VideoDriver)
  139. {
  140. createGUIAndScene();
  141. #ifdef _IRR_USE_CONSOLE_FONT_
  142. if (GUIEnvironment)
  143. {
  144. ConsoleFont = new gui::CGUIConsoleFont(this);
  145. gui::IGUISkin *skin = GUIEnvironment->getSkin();
  146. if (skin)
  147. {
  148. for (u32 i=0; i < gui::EGDF_COUNT; ++i)
  149. skin->setFont(ConsoleFont, gui::EGUI_DEFAULT_FONT(i));
  150. }
  151. }
  152. #endif
  153. }
  154. }
  155. //! destructor
  156. CIrrDeviceConsole::~CIrrDeviceConsole()
  157. {
  158. // GUI and scene are dropped in the stub
  159. if (CursorControl)
  160. {
  161. CursorControl->drop();
  162. CursorControl = 0;
  163. }
  164. if (ConsoleFont)
  165. {
  166. ConsoleFont->drop();
  167. ConsoleFont = 0;
  168. }
  169. #ifdef _IRR_VT100_CONSOLE_
  170. // reset terminal
  171. fprintf(OutFile, "%cc", 27);
  172. #endif
  173. }
  174. //! runs the device. Returns false if device wants to be deleted
  175. bool CIrrDeviceConsole::run()
  176. {
  177. // increment timer
  178. os::Timer::tick();
  179. // process Windows console input
  180. #ifdef _IRR_WINDOWS_NT_CONSOLE_
  181. INPUT_RECORD in;
  182. DWORD oldMode;
  183. DWORD count, waste;
  184. // get old input mode
  185. GetConsoleMode(WindowsSTDIn, &oldMode);
  186. SetConsoleMode(WindowsSTDIn, ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
  187. GetNumberOfConsoleInputEvents(WindowsSTDIn, &count);
  188. // read keyboard and mouse input
  189. while (count)
  190. {
  191. ReadConsoleInput(WindowsSTDIn, &in, 1, &waste );
  192. switch(in.EventType)
  193. {
  194. case KEY_EVENT:
  195. {
  196. SEvent e;
  197. e.EventType = EET_KEY_INPUT_EVENT;
  198. e.KeyInput.PressedDown = (in.Event.KeyEvent.bKeyDown == TRUE);
  199. e.KeyInput.Control = (in.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) != 0;
  200. e.KeyInput.Shift = (in.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) != 0;
  201. e.KeyInput.Key = EKEY_CODE(in.Event.KeyEvent.wVirtualKeyCode);
  202. e.KeyInput.Char = in.Event.KeyEvent.uChar.UnicodeChar;
  203. postEventFromUser(e);
  204. break;
  205. }
  206. case MOUSE_EVENT:
  207. {
  208. SEvent e;
  209. e.EventType = EET_MOUSE_INPUT_EVENT;
  210. e.MouseInput.X = in.Event.MouseEvent.dwMousePosition.X;
  211. e.MouseInput.Y = in.Event.MouseEvent.dwMousePosition.Y;
  212. e.MouseInput.Wheel = 0.f;
  213. e.MouseInput.ButtonStates =
  214. ( (in.Event.MouseEvent.dwButtonState & FROM_LEFT_1ST_BUTTON_PRESSED) ? EMBSM_LEFT : 0 ) |
  215. ( (in.Event.MouseEvent.dwButtonState & RIGHTMOST_BUTTON_PRESSED) ? EMBSM_RIGHT : 0 ) |
  216. ( (in.Event.MouseEvent.dwButtonState & FROM_LEFT_2ND_BUTTON_PRESSED) ? EMBSM_MIDDLE : 0 ) |
  217. ( (in.Event.MouseEvent.dwButtonState & FROM_LEFT_3RD_BUTTON_PRESSED) ? EMBSM_EXTRA1 : 0 ) |
  218. ( (in.Event.MouseEvent.dwButtonState & FROM_LEFT_4TH_BUTTON_PRESSED) ? EMBSM_EXTRA2 : 0 );
  219. if (in.Event.MouseEvent.dwEventFlags & MOUSE_MOVED)
  220. {
  221. CursorControl->setPosition(core::position2di(e.MouseInput.X, e.MouseInput.Y));
  222. // create mouse moved event
  223. e.MouseInput.Event = EMIE_MOUSE_MOVED;
  224. postEventFromUser(e);
  225. }
  226. if (in.Event.MouseEvent.dwEventFlags & MOUSE_WHEELED)
  227. {
  228. e.MouseInput.Event = EMIE_MOUSE_WHEEL;
  229. e.MouseInput.Wheel = (in.Event.MouseEvent.dwButtonState & 0xFF000000) ? -1.0f : 1.0f;
  230. postEventFromUser(e);
  231. }
  232. if ( (MouseButtonStates & EMBSM_LEFT) != (e.MouseInput.ButtonStates & EMBSM_LEFT) )
  233. {
  234. e.MouseInput.Event = (e.MouseInput.ButtonStates & EMBSM_LEFT) ? EMIE_LMOUSE_PRESSED_DOWN : EMIE_LMOUSE_LEFT_UP;
  235. postEventFromUser(e);
  236. }
  237. if ( (MouseButtonStates & EMBSM_RIGHT) != (e.MouseInput.ButtonStates & EMBSM_RIGHT) )
  238. {
  239. e.MouseInput.Event = (e.MouseInput.ButtonStates & EMBSM_RIGHT) ? EMIE_RMOUSE_PRESSED_DOWN : EMIE_RMOUSE_LEFT_UP;
  240. postEventFromUser(e);
  241. }
  242. if ( (MouseButtonStates & EMBSM_MIDDLE) != (e.MouseInput.ButtonStates & EMBSM_MIDDLE) )
  243. {
  244. e.MouseInput.Event = (e.MouseInput.ButtonStates & EMBSM_MIDDLE) ? EMIE_MMOUSE_PRESSED_DOWN : EMIE_MMOUSE_LEFT_UP;
  245. postEventFromUser(e);
  246. }
  247. // save current button states
  248. MouseButtonStates = e.MouseInput.ButtonStates;
  249. break;
  250. }
  251. case WINDOW_BUFFER_SIZE_EVENT:
  252. VideoDriver->OnResize(
  253. core::dimension2d<u32>(in.Event.WindowBufferSizeEvent.dwSize.X,
  254. in.Event.WindowBufferSizeEvent.dwSize.Y));
  255. break;
  256. case FOCUS_EVENT:
  257. IsWindowFocused = (in.Event.FocusEvent.bSetFocus == TRUE);
  258. break;
  259. default:
  260. break;
  261. }
  262. GetNumberOfConsoleInputEvents(WindowsSTDIn, &count);
  263. }
  264. // set input mode
  265. SetConsoleMode(WindowsSTDIn, oldMode);
  266. #else
  267. // todo: keyboard input from terminal in raw mode
  268. #endif
  269. return !Close;
  270. }
  271. //! Cause the device to temporarily pause execution and let other processes to run
  272. // This should bring down processor usage without major performance loss for Irrlicht
  273. void CIrrDeviceConsole::yield()
  274. {
  275. #ifdef _IRR_WINDOWS_API_
  276. Sleep(1);
  277. #else
  278. struct timespec ts = {0,0};
  279. nanosleep(&ts, NULL);
  280. #endif
  281. }
  282. //! Pause execution and let other processes to run for a specified amount of time.
  283. void CIrrDeviceConsole::sleep(u32 timeMs, bool pauseTimer)
  284. {
  285. const bool wasStopped = Timer ? Timer->isStopped() : true;
  286. #ifdef _IRR_WINDOWS_API_
  287. Sleep(timeMs);
  288. #else
  289. struct timespec ts;
  290. ts.tv_sec = (time_t) (timeMs / 1000);
  291. ts.tv_nsec = (long) (timeMs % 1000) * 1000000;
  292. if (pauseTimer && !wasStopped)
  293. Timer->stop();
  294. nanosleep(&ts, NULL);
  295. #endif
  296. if (pauseTimer && !wasStopped)
  297. Timer->start();
  298. }
  299. //! sets the caption of the window
  300. void CIrrDeviceConsole::setWindowCaption(const wchar_t* text)
  301. {
  302. #ifdef _IRR_WINDOWS_NT_CONSOLE_
  303. SetConsoleTitleW(text);
  304. #endif
  305. }
  306. //! returns if window is active. if not, nothing need to be drawn
  307. bool CIrrDeviceConsole::isWindowActive() const
  308. {
  309. // there is no window, but we always assume it is active
  310. return true;
  311. }
  312. //! returns if window has focus
  313. bool CIrrDeviceConsole::isWindowFocused() const
  314. {
  315. return IsWindowFocused;
  316. }
  317. //! returns if window is minimized
  318. bool CIrrDeviceConsole::isWindowMinimized() const
  319. {
  320. return false;
  321. }
  322. //! presents a surface in the client area
  323. bool CIrrDeviceConsole::present(video::IImage* surface, void* windowId, core::rect<s32>* src)
  324. {
  325. if (surface)
  326. {
  327. for (u32 y=0; y < surface->getDimension().Height; ++y)
  328. {
  329. for (u32 x=0; x< surface->getDimension().Width; ++x)
  330. {
  331. // get average pixel
  332. u32 avg = surface->getPixel(x,y).getAverage() * (ASCIIArtCharsCount-1);
  333. avg /= 255;
  334. OutputBuffer[y] [x] = ASCIIArtChars[avg];
  335. }
  336. }
  337. }
  338. #ifdef _IRR_USE_CONSOLE_FONT_
  339. for (u32 i=0; i< Text.size(); ++i)
  340. {
  341. s32 y = Text[i].Pos.Y;
  342. if ( y < (s32)OutputBuffer.size() && y > 0)
  343. for (u32 c=0; c < Text[i].Text.size() && c + Text[i].Pos.X < OutputBuffer[y].size(); ++c)
  344. //if (Text[i].Text[c] != ' ')
  345. OutputBuffer[y] [c+Text[i].Pos.X] = Text[i].Text[c];
  346. }
  347. Text.clear();
  348. #endif
  349. // draw output
  350. for (u32 y=0; y<OutputBuffer.size(); ++y)
  351. {
  352. setTextCursorPos(0,y);
  353. fprintf(OutFile, "%s", OutputBuffer[y].c_str());
  354. }
  355. return surface != 0;
  356. }
  357. //! notifies the device that it should close itself
  358. void CIrrDeviceConsole::closeDevice()
  359. {
  360. // return false next time we run()
  361. Close = true;
  362. }
  363. //! Sets if the window should be resizable in windowed mode.
  364. void CIrrDeviceConsole::setResizable(bool resize)
  365. {
  366. // do nothing
  367. }
  368. //! Minimize the window.
  369. void CIrrDeviceConsole::minimizeWindow()
  370. {
  371. // do nothing
  372. }
  373. //! Maximize window
  374. void CIrrDeviceConsole::maximizeWindow()
  375. {
  376. // do nothing
  377. }
  378. //! Restore original window size
  379. void CIrrDeviceConsole::restoreWindow()
  380. {
  381. // do nothing
  382. }
  383. void CIrrDeviceConsole::setTextCursorPos(s16 x, s16 y)
  384. {
  385. #ifdef _IRR_WINDOWS_NT_CONSOLE_
  386. // move WinNT cursor
  387. COORD Position;
  388. Position.X = x;
  389. Position.Y = y;
  390. SetConsoleCursorPosition(WindowsSTDOut, Position);
  391. #elif defined(_IRR_VT100_CONSOLE_)
  392. // send escape code
  393. fprintf(OutFile, "%c[%d;%dH", 27, y, x);
  394. #else
  395. // not implemented
  396. #endif
  397. }
  398. void CIrrDeviceConsole::addPostPresentText(s16 X, s16 Y, const wchar_t *text)
  399. {
  400. SPostPresentText p;
  401. p.Text = text;
  402. p.Pos.X = X;
  403. p.Pos.Y = Y;
  404. Text.push_back(p);
  405. }
  406. } // end namespace irr
  407. #endif // _IRR_COMPILE_WITH_CONSOLE_DEVICE_