gcsx_gui.cpp 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711
  1. /* GCSx
  2. ** GUI.CPP
  3. **
  4. ** Base gui classes and code
  5. */
  6. /*****************************************************************************
  7. ** Copyright (C) 2003-2006 Janson
  8. **
  9. ** This program is free software; you can redistribute it and/or modify
  10. ** it under the terms of the GNU General Public License as published by
  11. ** the Free Software Foundation; either version 2 of the License, or
  12. ** (at your option) any later version.
  13. **
  14. ** This program is distributed in the hope that it will be useful,
  15. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ** GNU General Public License for more details.
  18. **
  19. ** You should have received a copy of the GNU General Public License
  20. ** along with this program; if not, write to the Free Software
  21. ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
  22. *****************************************************************************/
  23. #include "all.h"
  24. SDLKey Desktop::keypadConvert[10] = {
  25. SDLK_INSERT,
  26. SDLK_END,
  27. SDLK_DOWN,
  28. SDLK_PAGEDOWN,
  29. SDLK_LEFT,
  30. SDLK_KP5,
  31. SDLK_RIGHT,
  32. SDLK_HOME,
  33. SDLK_UP,
  34. SDLK_PAGEUP,
  35. };
  36. int Window::nextId = 1;
  37. Desktop* desktop = NULL;
  38. WindowParent::~WindowParent() { start_func
  39. }
  40. WindowCollection::WindowCollection() : windowDataDisplay(), windowDataFocus() { start_func
  41. handleEvents = NULL;
  42. currentFocus = NULL;
  43. previousFocus = NULL;
  44. lastFocus = NULL;
  45. currentMouseFocus = NULL;
  46. lastMouseX = -1;
  47. lastMouseY = -1;
  48. modalPos = NULL;
  49. allowDesktopClick = 0;
  50. overallWidth = 0;
  51. overallHeight = 0;
  52. canvasX = 0;
  53. canvasY = 0;
  54. canvasWidth = 0;
  55. canvasHeight = 0;
  56. updateRect.w = 0;
  57. }
  58. WindowCollection::~WindowCollection() { start_func
  59. }
  60. void WindowCollection::addToUpdateRect(const Rect& add) { start_func
  61. boundRects(updateRect, add);
  62. }
  63. void WindowCollection::setEventHandler(int (*eventHandler)(const SDL_Event* event)) { start_func
  64. handleEvents = eventHandler;
  65. }
  66. // (verify that a window is still part of the collection)
  67. int WindowCollection::verifyWindow(const Window* target) const { start_func
  68. if (target == NULL) return 0;
  69. list<Window*>::const_iterator end = windowDataDisplay.end();
  70. for (list<Window*>::const_iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
  71. assert(*pos);
  72. if (*pos == target) return 1;
  73. }
  74. return 0;
  75. }
  76. Window* WindowCollection::verifyWindow(int targetId) { start_func
  77. if (targetId == 0) return NULL;
  78. list<Window*>::iterator end = windowDataDisplay.end();
  79. for (list<Window*>::iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
  80. assert(*pos);
  81. if ((*pos)->getId() == targetId) return *pos;
  82. }
  83. return NULL;
  84. }
  85. // Run an event
  86. // (after running an SDL_CLOSE event, any window pointers other than 'target' we have can't be trusted)
  87. int WindowCollection::windowRunEvent(Window* target, const SDL_Event* event) { start_func
  88. assert(target);
  89. assert(event);
  90. return target->event(currentFocus == target, event);
  91. }
  92. // (internal- find top window by coordinates)
  93. const Window* WindowCollection::findWindowByCoords(int x, int y) const { start_func
  94. list<Window*>::const_reverse_iterator end = windowDataDisplay.rend();
  95. for (list<Window*>::const_reverse_iterator pos = windowDataDisplay.rbegin(); pos != end; ++pos) {
  96. assert(*pos);
  97. if ((*pos)->isCoords(x, y)) return *pos;
  98. }
  99. return NULL;
  100. }
  101. Window* WindowCollection::findWindowByCoords(int x, int y) { start_func
  102. list<Window*>::reverse_iterator end = windowDataDisplay.rend();
  103. for (list<Window*>::reverse_iterator pos = windowDataDisplay.rbegin(); pos != end; ++pos) {
  104. assert(*pos);
  105. if ((*pos)->isCoords(x, y)) return *pos;
  106. }
  107. return NULL;
  108. }
  109. // (internal- tell new window it has mouse focus, if applicable; revert to normal mouse pointer)
  110. Window* WindowCollection::recheckMouseFocus(int sendMoveEvent) { start_func
  111. Window* window = findWindowByCoords(lastMouseX, lastMouseY);
  112. // Refusing?
  113. if (window) {
  114. if (window->refuseAll()) window = NULL;
  115. }
  116. // Disallow mouse focus on anything but the current modal window OR a window
  117. // sorted above it (if not on modal window, go to NULL)
  118. if (modalPos) {
  119. if (!window) {
  120. window = NULL;
  121. }
  122. else if ((window->windowSort() <= Window::WINDOWSORT_MODAL) && (window != modalPos)) {
  123. window = NULL;
  124. }
  125. }
  126. if (window != currentMouseFocus) {
  127. desktop->focusEvent(SDL_MOUSEFOCUS, 0, currentMouseFocus);
  128. desktop->focusEvent(SDL_MOUSEFOCUS, 1, window);
  129. currentMouseFocus = window;
  130. selectMouse(MOUSE_NORMAL);
  131. if (sendMoveEvent) {
  132. SDL_Event customEvent;
  133. customEvent.type = SDL_MOUSEMOTION;
  134. customEvent.motion.x = lastMouseX;
  135. customEvent.motion.y = lastMouseY;
  136. customEvent.motion.state = SDL_GetMouseState(NULL, NULL);
  137. customEvent.motion.xrel = 0;
  138. customEvent.motion.yrel = 0;
  139. insertEvent(&customEvent);
  140. }
  141. }
  142. return window;
  143. }
  144. // (internal- tell new window it has input focus)
  145. int WindowCollection::changeInputFocus(Window* window, int toTop) { start_func
  146. if (window != currentFocus) {
  147. // Refusing?
  148. if (window) {
  149. if (window->refuseAll()) return 0;
  150. }
  151. if ((window) && (toTop)) {
  152. // Bring to top of focus stack first
  153. bringToTop(window);
  154. // bringToTop called us, so we're done
  155. return 1;
  156. }
  157. // Always announce desktop losing focus
  158. if ((!currentFocus) && (allowDesktopClick) && (this == desktop)) desktop->broadcastEvent(SDL_SPECIAL, SDL_DESKTOPINACTIVE);
  159. // Possibilities-
  160. // tempFocus to tempFocus- normal focus change
  161. // normal/blank to normal/blank- normal focus change, kill previousfocus
  162. // normal/blank to tempFocus- old focus doesn't "lose" it
  163. // tempFocus to normal/blank- both temp and previous focus lose it, even if changing
  164. // back to the previous, it still must lose it temp.
  165. int from = 0;
  166. int to = 0;
  167. if (currentFocus) {
  168. if (currentFocus->tempFocus()) from = 1;
  169. }
  170. if (window) {
  171. if (window->tempFocus()) to = 1;
  172. }
  173. if (from == to) {
  174. desktop->focusEvent(SDL_INPUTFOCUS, 0, currentFocus);
  175. // From normal to normal, if we STILL have a previous focus...
  176. if (!to) {
  177. // Kill it. If it's the new focus window, tho, don't send an event.
  178. if ((previousFocus != window) && (previousFocus)) desktop->focusEvent(SDL_INPUTFOCUS, 0, previousFocus);
  179. previousFocus = NULL;
  180. }
  181. }
  182. else if (from == 0) {
  183. previousFocus = currentFocus;
  184. }
  185. else {
  186. desktop->focusEvent(SDL_INPUTFOCUS, 0, previousFocus);
  187. desktop->focusEvent(SDL_INPUTFOCUS, 0, currentFocus);
  188. previousFocus = NULL;
  189. }
  190. desktop->focusEvent(SDL_INPUTFOCUS, 1, window);
  191. currentFocus = window;
  192. // Always announce desktop gaining focus
  193. if ((!currentFocus) && (allowDesktopClick) && (this == desktop)) desktop->broadcastEvent(SDL_SPECIAL, SDL_DESKTOPACTIVE);
  194. return 1;
  195. }
  196. return 0;
  197. }
  198. void WindowCollection::resolutionChange(int fromW, int fromH, int fromBpp, int toW, int toH, int toBpp) { start_func
  199. list<Window*>::iterator end = windowDataDisplay.end();
  200. for (list<Window*>::iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
  201. assert(*pos);
  202. (*pos)->resolutionChange(fromW, fromH, fromBpp, toW, toH, toBpp);
  203. }
  204. // Call AFTER resizing windows, as the docked ones have now rearranged themselves
  205. adjustCanvas();
  206. }
  207. // (internal- adjust our canvas size/pos to match any currently docked windows)
  208. void WindowCollection::adjustCanvas() { start_func
  209. // (skip if we aren't using this feature)
  210. if ((overallWidth) && (overallHeight)) {
  211. canvasX = 0;
  212. canvasY = 0;
  213. int canvasRight = overallWidth;
  214. int canvasBottom = overallHeight;
  215. // Scan all windows for docked ones
  216. list<Window*>::iterator end = windowDataDisplay.end();
  217. for (list<Window*>::iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
  218. Window* wpos = *pos;
  219. assert(wpos);
  220. if (wpos->isDocked()) {
  221. // Determine what edge it is docked at based on what screen edges it touches
  222. // If a window touches two opposite edges, neither is the docking edge
  223. // (for example, menubar touches left/right but is docked at the top)
  224. if ((wpos->getX() <= 0) && (wpos->getX() + wpos->getWidth() < overallWidth)) {
  225. // Left edge- increase canvasX if needed
  226. int size = wpos->getWidth() + wpos->getX();
  227. if (size > canvasX) canvasX = size;
  228. }
  229. if ((wpos->getX() + wpos->getWidth() >= overallWidth) && (wpos->getX() > 0)) {
  230. // Right edge- decrease canvasRight if needed
  231. if (wpos->getX() < canvasRight) canvasRight = wpos->getX();
  232. }
  233. if ((wpos->getY() <= 0) && (wpos->getY() + wpos->getHeight() < overallHeight)) {
  234. // Top edge- increase canvasY if needed
  235. int size = wpos->getHeight() + wpos->getY();
  236. if (size > canvasY) canvasY = size;
  237. }
  238. if ((wpos->getY() + wpos->getHeight() >= overallHeight) && (wpos->getY() > 0)) {
  239. // Bottom edge- decrease canvasBottom if needed
  240. if (wpos->getY() < canvasBottom) canvasBottom = wpos->getY();
  241. }
  242. }
  243. }
  244. canvasWidth = canvasRight - canvasX;
  245. canvasHeight = canvasBottom - canvasY;
  246. // Here, we "force" any frame windows to be within our confines
  247. for (list<Window*>::iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
  248. Window* wpos = *pos;
  249. assert(wpos);
  250. if (typeid(*wpos) == typeid(FrameWindow)) {
  251. dynamic_cast<FrameWindow*>(wpos)->redoSize();
  252. }
  253. }
  254. }
  255. }
  256. void Window::move(int xPos, int yPos) { start_func
  257. int prevX = x;
  258. int prevY = y;
  259. x = xPos;
  260. y = yPos;
  261. // @TODO: At some point we might be able to scroll data and just redraw
  262. // partial or not at all
  263. setDirty(1);
  264. if (parent) parent->childMoved(prevX, prevY, x, y, this);
  265. }
  266. void Window::resize(int newWidth, int newHeight, int newViewWidth, int newViewHeight, int fromParent) { start_func
  267. int updateParent = 0;
  268. int oldWidth = width;
  269. int oldHeight = height;
  270. if (newViewWidth < 0) newViewWidth = viewWidth;
  271. if (newViewHeight < 0) newViewHeight = viewHeight;
  272. // Compare so we don't resize if not needed and to prevent unneeded recursion
  273. if ((newWidth != width) || (newHeight != height) ||
  274. (newViewWidth != viewWidth) || (newViewHeight != viewHeight)) {
  275. if ((newWidth != width) || (newHeight != height)) updateParent = 1;
  276. viewWidth = newViewWidth;
  277. viewHeight = newViewHeight;
  278. width = newWidth;
  279. height = newHeight;
  280. setDirty(1);
  281. // Update parent, including FrameWindow or WidgetScroll if there is one
  282. if ((parent) && (!fromParent) && (updateParent)) {
  283. parent->childResized(oldWidth, oldHeight, width, height, this);
  284. if (typeid(*parent) == typeid(FrameWindow)) {
  285. dynamic_cast<FrameWindow*>(parent)->updateClientSize();
  286. }
  287. else if (typeid(*parent) == typeid(WidgetScroll)) {
  288. dynamic_cast<WidgetScroll*>(parent)->updateClientSize();
  289. }
  290. }
  291. }
  292. if ((width > 0) && (height > 0) && (parent) && (viewWidth != 0) && (viewHeight != 0)) visible = 1;
  293. else visible = 0;
  294. }
  295. int Window::attemptClose() { start_func
  296. return 1;
  297. }
  298. /* Not currently used or supported
  299. int Window::usesAlpha() const { start_func
  300. return 0;
  301. }
  302. */
  303. int Window::isDocked() const { start_func
  304. return 0;
  305. }
  306. int Window::tempFocus() const { start_func
  307. return 0;
  308. }
  309. int Window::refuseAll() const { start_func
  310. return 0;
  311. }
  312. Window::CommandSupport Window::supportsCommand(int code) const { start_func
  313. return COMMAND_HIDE;
  314. }
  315. Window::WindowType Window::windowType() const { start_func
  316. return WINDOW_UNKNOWN;
  317. }
  318. int Window::wantsToBeDeleted() const { start_func
  319. return 1;
  320. }
  321. Window::WindowSort Window::windowSort() const { start_func
  322. return WINDOWSORT_NORMAL;
  323. }
  324. int Window::isOnDesktop() const { start_func
  325. if (parent) {
  326. if (parent == desktop) return desktop->verifyWindow(this);
  327. else return dynamic_cast<Window*>(parent)->isOnDesktop();
  328. }
  329. return 0;
  330. }
  331. void Window::closeWindow(int skipAttempt) { start_func
  332. if (isOnDesktop()) {
  333. if ((parent) && (parent != desktop)) {
  334. dynamic_cast<FrameWindow*>(parent)->closeWindow(skipAttempt);
  335. }
  336. else {
  337. if (!skipAttempt) {
  338. if (!attemptClose()) return;
  339. }
  340. desktop->focusEvent(SDL_CLOSE, 0, this);
  341. }
  342. }
  343. }
  344. const char* Window::tooltip(int xPos, int yPos) const { start_func
  345. return NULL;
  346. }
  347. int Window::isCoords(int xPos, int yPos) const { start_func
  348. if ((xPos >= x) && (yPos >= y) && (xPos < x + width) && (yPos < y + height)) return 1;
  349. return 0;
  350. }
  351. int Window::getId() const { start_func
  352. return id;
  353. }
  354. int Window::getX() const { start_func
  355. return x;
  356. }
  357. int Window::getY() const { start_func
  358. return y;
  359. }
  360. int Window::getScreenX() const { start_func
  361. if (parent) return parent->getScreenX() + x;
  362. return x;
  363. }
  364. int Window::getScreenY() const { start_func
  365. if (parent) return parent->getScreenY() + y;
  366. return y;
  367. }
  368. int Window::getWidth() const { start_func
  369. return width;
  370. }
  371. int Window::getHeight() const { start_func
  372. return height;
  373. }
  374. void Window::getRect(Rect& rect) const { start_func
  375. rect.x = x;
  376. rect.y = y;
  377. rect.w = width;
  378. rect.h = height;
  379. }
  380. void Window::undoNotify(int undoType, int undoItemId, int undoItemSubId, int uX, int uY, int uW, int uH) { start_func
  381. }
  382. Window::Window() { start_func
  383. parent = NULL;
  384. parentNotify = NULL;
  385. visible = 0;
  386. id = nextId++;
  387. width = height = -1;
  388. viewWidth = viewHeight = -1;
  389. dirty = 1;
  390. totalDirty = 1;
  391. childDirty = 0;
  392. }
  393. Window::~Window() { start_func
  394. if (parent) parent->childDeleted(this);
  395. if ((parentNotify) && (parentNotify != parent)) parentNotify->childDeleted(this);
  396. cleanEvents(this);
  397. }
  398. #ifndef NDEBUG
  399. const char* Window::debugDump() const { start_func
  400. return "(untyped window)";
  401. }
  402. #endif
  403. void Window::setDirty(int total) { start_func
  404. if (total) totalDirty = 1;
  405. if (!dirty) {
  406. dirty = 1;
  407. if (parent) parent->setChildDirty();
  408. }
  409. }
  410. void Window::childMoved(int fromX, int fromY, int toX, int toY, Window* child) { start_func
  411. assert(child);
  412. }
  413. void Window::childResized(int fromW, int fromH, int toW, int toH, Window* child) { start_func
  414. assert(child);
  415. }
  416. void Window::childDeleted(Window* child) { start_func
  417. assert(child);
  418. }
  419. void Window::childModified(Window* child) { start_func
  420. assert(child);
  421. if (parent) parent->childModified(child);
  422. }
  423. void Window::siblingModified(Window* sibling) { start_func
  424. assert(sibling);
  425. }
  426. void Window::setChildDirty() { start_func
  427. childDirty = 1;
  428. if (parent) parent->setChildDirty();
  429. }
  430. int Window::isDirty() const { start_func
  431. // Don't check totalDirty, it's never set if dirty isn't set
  432. return dirty || childDirty;
  433. }
  434. void Window::setParent(WindowParent* wParent) { start_func
  435. parent = wParent;
  436. if ((width > 0) && (height > 0) && (parent) && (viewWidth != 0) && (viewHeight != 0)) visible = 1;
  437. else visible = 0;
  438. if ((dirty) && (parent)) parent->setChildDirty();
  439. }
  440. void Window::setParentNotify(WindowParent* wParentNotify) { start_func
  441. parentNotify = wParentNotify;
  442. }
  443. void Window::resolutionChange(int fromW, int fromH, int fromBpp, int toW, int toH, int toBpp) { start_func
  444. // Default does nothing
  445. }
  446. void WindowCollection::addWindow(Window* item, int giveFocus) { start_func
  447. assert(item);
  448. // Abort if already part of our collection
  449. if (verifyWindow(item)) return;
  450. Window::WindowSort sortType = item->windowSort();
  451. // Scan through windows until we find one of equal/lower sort value
  452. // (can't use reverse iterator)
  453. list<Window*>::iterator pos = windowDataDisplay.end();
  454. list<Window*>::iterator begin = windowDataDisplay.begin();
  455. while (pos != begin) {
  456. --pos;
  457. assert(*pos);
  458. if ((*pos)->windowSort() <= sortType) {
  459. // Insert after this one
  460. ++pos; // Move one element back towards the end of the list
  461. windowDataDisplay.insert(pos, item);
  462. break;
  463. }
  464. }
  465. if (pos == windowDataDisplay.begin()) {
  466. // Insert at beginning
  467. windowDataDisplay.push_front(item);
  468. }
  469. // Add to focus stack at beginning or end
  470. if ((giveFocus) || (sortType == Window::WINDOWSORT_MODAL)) {
  471. windowDataFocus.push_back(item);
  472. }
  473. else {
  474. windowDataFocus.push_front(item);
  475. }
  476. item->setParent(this);
  477. Rect toUpdate;
  478. item->getRect(toUpdate);
  479. addToUpdateRect(toUpdate);
  480. if (item->isDocked()) adjustCanvas();
  481. recheckMouseFocus(1);
  482. if ((giveFocus) || (sortType == Window::WINDOWSORT_MODAL)) changeInputFocus(item);
  483. if (sortType == Window::WINDOWSORT_MODAL) modalPos = item;
  484. }
  485. void WindowCollection::removeWindow(Window* item) { start_func
  486. assert(item);
  487. Window* highestModal = NULL;
  488. // Main list of windows
  489. // (can't use reverse iterators)
  490. // Note that we purposely use windowDataDisplay here
  491. // and windowDataFocus in deleteAllWindows
  492. list<Window*>::iterator pos = windowDataDisplay.end();
  493. list<Window*>::iterator found = windowDataDisplay.end();
  494. list<Window*>::iterator begin = windowDataDisplay.begin();
  495. while (pos != begin) {
  496. --pos;
  497. assert(*pos);
  498. if (*pos == item) {
  499. found = pos;
  500. }
  501. else if (!highestModal) {
  502. if ((*pos)->windowSort() == Window::WINDOWSORT_MODAL) highestModal = *pos;
  503. }
  504. }
  505. if (found != windowDataDisplay.end()) {
  506. // Found- remove
  507. item->setParent(NULL);
  508. if (previousFocus == item) previousFocus = NULL;
  509. Rect toUpdate;
  510. item->getRect(toUpdate);
  511. addToUpdateRect(toUpdate);
  512. windowDataDisplay.erase(found);
  513. cleanEvents(item);
  514. // Also remove from focus stack
  515. for (pos = windowDataFocus.begin(); pos != windowDataFocus.end(); ++pos) {
  516. assert(*pos);
  517. if (*pos == item) {
  518. windowDataFocus.erase(pos);
  519. break;
  520. }
  521. }
  522. if (item->isDocked()) adjustCanvas();
  523. if (currentMouseFocus == item) {
  524. currentMouseFocus = NULL;
  525. recheckMouseFocus(1);
  526. }
  527. if (modalPos == item) {
  528. modalPos = highestModal;
  529. }
  530. if (currentFocus == item) {
  531. // (remove before swapping, to prevent it from showing up in previousFocus)
  532. currentFocus = NULL;
  533. if (previousFocus) changeInputFocus(previousFocus);
  534. else if (!windowDataFocus.empty()) changeInputFocus(*(--windowDataFocus.end()));
  535. else changeInputFocus(NULL);
  536. }
  537. }
  538. assert(item != currentFocus);
  539. assert(item != previousFocus);
  540. assert(item != currentMouseFocus);
  541. }
  542. // pos = 0 for first, 1 for second, etc.
  543. Window* WindowCollection::findWindow(Window::WindowType type, int pos) { start_func
  544. assert(pos >= 0);
  545. assert(type > Window::WINDOW_UNKNOWN);
  546. assert(type <= Window::WINDOW_LASTTYPE);
  547. // This function by definition searches focus stack (see header)
  548. list<Window*>::iterator iend = windowDataFocus.end();
  549. for (list<Window*>::iterator ipos = windowDataFocus.begin(); ipos != iend; ++ipos) {
  550. assert(*ipos);
  551. if ((*ipos)->windowType() == type) {
  552. if (pos == 0) return *ipos;
  553. --pos;
  554. }
  555. }
  556. return NULL;
  557. }
  558. Window* WindowCollection::findFocusWindow() { start_func
  559. return currentFocus;
  560. }
  561. Window* WindowCollection::findPreviousFocusWindow() { start_func
  562. if (previousFocus) return previousFocus;
  563. return currentFocus;
  564. }
  565. void WindowCollection::bringToTop(Window* item, int giveFocus) { start_func
  566. if (!item) {
  567. if ((allowDesktopClick) && (!modalPos)) changeInputFocus(NULL, 0);
  568. return;
  569. }
  570. int found = 0;
  571. // First, find and remove...
  572. list<Window*>::iterator end = windowDataDisplay.end();
  573. for (list<Window*>::iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
  574. assert(*pos);
  575. if (*pos == item) {
  576. windowDataDisplay.erase(pos);
  577. found = 1;
  578. break;
  579. }
  580. }
  581. // Then, insert
  582. if (found) {
  583. int sortType = item->windowSort();
  584. // (can't use reverse iterator)
  585. list<Window*>::iterator pos = windowDataDisplay.end();
  586. list<Window*>::iterator begin = windowDataDisplay.begin();
  587. while (pos != begin) {
  588. --pos;
  589. assert(*pos);
  590. if ((*pos)->windowSort() <= sortType) {
  591. // Insert after this one
  592. ++pos; // Move one element back towards the end of the list
  593. windowDataDisplay.insert(pos, item);
  594. break;
  595. }
  596. }
  597. if (pos == windowDataDisplay.begin()) {
  598. // Insert at beginning
  599. windowDataDisplay.push_front(item);
  600. }
  601. // Find and move in focus stack too?
  602. if (giveFocus) {
  603. for (pos = windowDataFocus.begin(); pos != windowDataFocus.end(); ++pos) {
  604. assert(*pos);
  605. if (*pos == item) {
  606. windowDataFocus.erase(pos);
  607. windowDataFocus.push_back(item);
  608. break;
  609. }
  610. }
  611. changeInputFocus(item, 0); // Don't recurse to bringToTop again
  612. }
  613. Rect toUpdate;
  614. item->getRect(toUpdate);
  615. addToUpdateRect(toUpdate);
  616. recheckMouseFocus(1);
  617. }
  618. }
  619. void WindowCollection::sendToBottom(Window* item) { start_func
  620. assert(item);
  621. int found = 0;
  622. // First, find and remove...
  623. list<Window*>::iterator end = windowDataDisplay.end();
  624. for (list<Window*>::iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
  625. assert(*pos);
  626. if (*pos == item) {
  627. windowDataDisplay.erase(pos);
  628. found = 1;
  629. break;
  630. }
  631. }
  632. // Then, insert
  633. if (found) {
  634. Window::WindowSort sortType = item->windowSort();
  635. list<Window*>::iterator end = windowDataDisplay.end();
  636. list<Window*>::iterator pos;
  637. for (pos = windowDataDisplay.begin(); pos != end; ++pos) {
  638. assert(*pos);
  639. if ((*pos)->windowSort() >= sortType) {
  640. // Insert before this one
  641. windowDataDisplay.insert(pos, item);
  642. break;
  643. }
  644. }
  645. if (pos == windowDataDisplay.end()) {
  646. // Insert at end
  647. windowDataDisplay.push_back(item);
  648. }
  649. // Now, find in focus stack, move to bottom
  650. end = windowDataFocus.end();
  651. for (pos = windowDataFocus.begin(); pos != end; ++pos) {
  652. assert(*pos);
  653. if (*pos == item) {
  654. windowDataFocus.erase(pos);
  655. windowDataFocus.push_front(item);
  656. break;
  657. }
  658. }
  659. if (currentFocus == item) {
  660. if (previousFocus) changeInputFocus(previousFocus);
  661. else changeInputFocus(*(--windowDataFocus.end()));
  662. }
  663. Rect toUpdate;
  664. item->getRect(toUpdate);
  665. addToUpdateRect(toUpdate);
  666. }
  667. }
  668. int WindowCollection::handleEvent(const SDL_Event* event) { start_func
  669. Window* window = NULL;
  670. Window* exempt = NULL;
  671. int broadcast = 0;
  672. assert(event);
  673. SDL_Event eventCopy = *event;
  674. // Select appropriate window
  675. switch (eventCopy.type) {
  676. case SDL_NOEVENT:
  677. return 0;
  678. case SDL_QUIT:
  679. case SDL_SYSKEY:
  680. case SDL_SPECIAL:
  681. case SDL_OBJECTCHANGE:
  682. // (exempt window may exist)
  683. exempt = (Window*)eventCopy.user.data2;
  684. case SDL_ACTIVEEVENT:
  685. case SDL_VIDEORESIZE:
  686. case SDL_VIDEOEXPOSE:
  687. broadcast = 1;
  688. break;
  689. case SDL_CLOSE:
  690. case SDL_INPUTFOCUS:
  691. case SDL_MOUSEFOCUS:
  692. // Window is defined as part of the event
  693. window = (Window*)eventCopy.user.data1;
  694. // (global SDL_CLOSE events not currently used, but can be supported)
  695. // if (window == NULL) broadcast = 1;
  696. // This will catch it if it recurses/loops back to us by mistake
  697. assert(window);
  698. eventCopy.user.data1 = NULL;
  699. break;
  700. case SDL_COMMAND:
  701. case SDL_KEYDOWN:
  702. case SDL_KEYUP:
  703. window = currentFocus;
  704. if (!window) {
  705. // @TODO: don't break if we ever start broadcasting keyups/commandreleases
  706. if (eventCopy.type == SDL_KEYUP) break;
  707. if (eventCopy.type == SDL_KEYDOWN) eventCopy.type = SDL_SYSKEY;
  708. broadcast = 1;
  709. }
  710. break;
  711. case SDL_MOUSEMOTION:
  712. lastMouseX = eventCopy.motion.x;
  713. lastMouseY = eventCopy.motion.y;
  714. // If a button is pressed, we keep the previous focus indefinitely
  715. if ((eventCopy.motion.state & SDL_BUTTON_LMASK) || (eventCopy.motion.state & SDL_BUTTON_RMASK)) {
  716. window = currentMouseFocus;
  717. }
  718. else {
  719. window = recheckMouseFocus();
  720. }
  721. // Adjust coordinates to 0-based for window
  722. if (window) {
  723. eventCopy.motion.x -= window->getX();
  724. eventCopy.motion.y -= window->getY();
  725. }
  726. break;
  727. case SDL_MOUSEBUTTONUP:
  728. // Mouse release also retains previous window focus
  729. lastMouseX = eventCopy.button.x;
  730. lastMouseY = eventCopy.button.y;
  731. window = currentMouseFocus;
  732. // Adjust coordinates to 0-based for window
  733. if (window) {
  734. eventCopy.button.x -= window->getX();
  735. eventCopy.button.y -= window->getY();
  736. }
  737. break;
  738. case SDL_MOUSEBUTTONDBL:
  739. case SDL_MOUSEBUTTONDOWN:
  740. lastMouseX = eventCopy.button.x;
  741. lastMouseY = eventCopy.button.y;
  742. window = recheckMouseFocus();
  743. // Adjust coordinates to 0-based for window
  744. if ((window) || ((allowDesktopClick) && (!modalPos))) {
  745. // No focus on wheels
  746. if ((event->button.button != SDL_BUTTON_WHEELUP) &&
  747. (event->button.button != SDL_BUTTON_WHEELDOWN)) {
  748. changeInputFocus(window);
  749. }
  750. if (window) {
  751. eventCopy.button.x -= window->getX();
  752. eventCopy.button.y -= window->getY();
  753. }
  754. }
  755. else if (currentFocus) {
  756. // NULL focus will take away focus from a tempfocus window (popup)
  757. if ((currentFocus->tempFocus()) &&
  758. (event->button.button != SDL_BUTTON_WHEELUP) &&
  759. (event->button.button != SDL_BUTTON_WHEELDOWN)) {
  760. if (previousFocus) changeInputFocus(previousFocus);
  761. else changeInputFocus(NULL);
  762. }
  763. }
  764. break;
  765. default:
  766. return 0;
  767. }
  768. // Propogate event
  769. if (window) {
  770. // Don't recurse (never broadcast in this case, either)
  771. if ((void*)window == (void*)this) return 0;
  772. // Refusing?
  773. if (!window->refuseAll()) {
  774. // single window event, is allowed to close
  775. if (windowRunEvent(window, &eventCopy)) {
  776. if ((eventCopy.type == SDL_CLOSE) && (window->wantsToBeDeleted())) {
  777. delete window;
  778. }
  779. return 1;
  780. }
  781. }
  782. // If window is a floating panel of another window, attempt key/cmd there
  783. if ((typeid(*window) == typeid(FrameWindow)) && (!modalPos)) {
  784. if (dynamic_cast<FrameWindow*>(window)->getFramePanel()) {
  785. if ((eventCopy.type == SDL_KEYDOWN) || (eventCopy.type == SDL_KEYUP) || (eventCopy.type == SDL_COMMAND)) {
  786. if (windowRunEvent(dynamic_cast<FrameWindow*>(window)->getFramePanel(), &eventCopy)) return 1;
  787. }
  788. }
  789. }
  790. // Keypresses now broadcast unless modal window open
  791. if ((eventCopy.type == SDL_KEYDOWN) && (!modalPos)) {
  792. eventCopy.type = SDL_SYSKEY;
  793. broadcast = 1;
  794. }
  795. // If modal window, run as shortcut
  796. else if ((eventCopy.type == SDL_KEYDOWN) && (this == desktop)) {
  797. int count = config->readShortcut(eventCopy.key.keysym.sym, eventCopy.key.keysym.mod, 0);
  798. if (count) {
  799. // Recurse through all potential shortcuts
  800. for (int pos = 1; pos <= count; ++pos) {
  801. int cmd = config->readShortcut(eventCopy.key.keysym.sym, eventCopy.key.keysym.mod, pos);
  802. // Stop when one works
  803. if (desktop->broadcastEvent(SDL_COMMAND, cmd, NULL, NULL, 1)) {
  804. desktop->lastShortcutKey = eventCopy.key.keysym.sym;
  805. return 1;
  806. }
  807. }
  808. }
  809. return 0;
  810. }
  811. // SDL_COMMAND goes to global event unless modal window open
  812. else if ((handleEvents) && (eventCopy.type == SDL_COMMAND) && (!modalPos) && (this == desktop)) {
  813. return handleEvents(&eventCopy);
  814. }
  815. // (anything else is just plain done)
  816. else return 0;
  817. }
  818. if (broadcast) {
  819. int result = 0;
  820. list<Window*>::reverse_iterator pos;
  821. list<Window*>::reverse_iterator next;
  822. list<Window*>::reverse_iterator end = windowDataDisplay.rend();
  823. // Reverse order, because if a window moves, it should be towards the end
  824. for (pos = windowDataDisplay.rbegin(); pos != end; pos = next) {
  825. assert(*pos);
  826. if ((eventCopy.type == SDL_SPECIAL) && (eventCopy.user.code == SDL_PREQUIT) && (desktop->cancelQuitState())) break;
  827. // Note the next element in sequence now, before we run event
  828. next = pos;
  829. ++next;
  830. // Don't recurse
  831. if ((void*)(*pos) == (void*)this) return 0;
  832. // Refusing?
  833. if ((*pos)->refuseAll()) continue;
  834. // Exempt?
  835. if (*pos == exempt) continue;
  836. // Run event- if window closes or moves to top, we already have next item
  837. if (windowRunEvent(*pos, &eventCopy)) result = 1;
  838. // If a SDL_SYSKEY event that was used, we're done
  839. if ((eventCopy.type == SDL_SYSKEY) && (result)) break;
  840. }
  841. // Unused SDL_SYSKEY events attempts to run as shortcut now
  842. if ((eventCopy.type == SDL_SYSKEY) && (!result) && (this == desktop)) {
  843. int count = config->readShortcut(eventCopy.key.keysym.sym, eventCopy.key.keysym.mod, 0);
  844. if (count) {
  845. // Close all popup menus at any level
  846. PopupMenu* menu = dynamic_cast<PopupMenu*>(desktop->findWindow(Window::WINDOW_MENUBAR, 0));
  847. if (menu) menu->closeAllSubmenus(0);
  848. // Recurse through all potential shortcuts
  849. for (int pos = 1; pos <= count; ++pos) {
  850. int cmd = config->readShortcut(eventCopy.key.keysym.sym, eventCopy.key.keysym.mod, pos);
  851. // Stop when one works
  852. if (desktop->broadcastEvent(SDL_COMMAND, cmd, NULL, NULL, 1)) {
  853. desktop->lastShortcutKey = eventCopy.key.keysym.sym;
  854. result = 1;
  855. break;
  856. }
  857. }
  858. }
  859. }
  860. // Goes to event handler, unless-
  861. // SDL_COMMAND or SDL_SYSKEY that was used, or
  862. // SDL_CLOSE
  863. // (global SDL_CLOSE events not currently used, but can be supported)
  864. if ((handleEvents) &&
  865. // (eventCopy.type != SDL_CLOSE) &&
  866. (((eventCopy.type != SDL_COMMAND) && (eventCopy.type != SDL_SYSKEY)) || (!result))) {
  867. if (handleEvents(&eventCopy)) result = 1;
  868. }
  869. return result;
  870. }
  871. return 0;
  872. }
  873. void Desktop::setGameLoop(void (*gameLoop)(int)) { start_func
  874. gameLoopH = gameLoop;
  875. allowDesktopClick = gameLoop ? 1 : 0;
  876. }
  877. void Desktop::setSupportsHandler(Window::CommandSupport (*supportsHandler)(int code)) { start_func
  878. supportsCommandH = supportsHandler;
  879. }
  880. Window::CommandSupport Desktop::supportsCommand(int code, Window::CommandSupport prior) const { start_func
  881. if (supportsCommandH) {
  882. Window::CommandSupport sc = supportsCommandH(code);
  883. if ((prior == Window::COMMAND_HIDE) || (sc & Window::COMMAND_ENABLE)) return sc;
  884. }
  885. return prior;
  886. }
  887. void Desktop::preventDoubleClick() { start_func
  888. dclickTicks = 0;
  889. }
  890. void Desktop::updateScreen() { start_func
  891. // Restore mouse (screen should have NULL clip right now)
  892. unblitMouse();
  893. // Screen clip
  894. Rect screenClip = { 0, 0, overallWidth, overallHeight };
  895. // Start with updateRect at it's current status
  896. if (updateRect.w) {
  897. // Ensure clipped to screen size
  898. intersectRects(updateRect, screenClip);
  899. // Fill with background (screen should have NULL clip right now)
  900. SDL_FillRect(getScreen(), &updateRect, convertRGB(guiRGB[backgroundColorIndex], backgroundAlpha));
  901. if (backgroundImage) {
  902. Rect imageClip = { bkDestX, bkDestY, bkSrcW, bkSrcH };
  903. if (intersectRects(imageClip, updateRect)) {
  904. blit(bkSrcX + imageClip.x - bkDestX, bkSrcY + imageClip.y - bkDestY,
  905. backgroundImage, imageClip.x, imageClip.y, getScreen(), imageClip.w, imageClip.h);
  906. }
  907. }
  908. }
  909. SDL_Surface* screen = getScreen();
  910. if ((childDirty) || (updateRect.w)) {
  911. list<Window*>::iterator end = windowDataDisplay.end();
  912. for (list<Window*>::iterator pos = windowDataDisplay.begin(); pos != end; ++pos) {
  913. assert(*pos);
  914. // Start with clip area of entire window
  915. Rect clipDirty;
  916. (*pos)->getRect(clipDirty);
  917. // Actual clip to within screen size
  918. Rect clipTotal = clipDirty;
  919. // If this intersects with our current update area OR window is dirty
  920. if ((intersectRects(clipDirty, updateRect)) || ((*pos)->isDirty())) {
  921. // ...then display window (we only request area that matches our update)
  922. (*pos)->display(screen, clipDirty, clipTotal, 0, 0);
  923. // and add in anything it did update to our update area
  924. boundRects(updateRect, clipDirty);
  925. }
  926. }
  927. }
  928. childDirty = 0;
  929. SDL_SetClipRect(screen, (SDL_Rect*)NULL);
  930. blitMouse();
  931. intersectRects(updateRect, screenClip);
  932. // Call game loop, if any
  933. if (gameLoopH) {
  934. // If desktop can be clicked AND is active, pass keys to game
  935. gameLoopH((allowDesktopClick) && (currentFocus == NULL));
  936. }
  937. // Update our screen now
  938. screenFlip(&updateRect);
  939. postGLScreenFlip();
  940. // Clear updateRect for next time
  941. updateRect.w = 0;
  942. }
  943. int Desktop::eventLoop() { start_func
  944. Window* modalLoopPos = NULL;
  945. SDL_Event event;
  946. SDL_Event customEvent;
  947. customEvent.type = SDL_SPECIAL;
  948. customEvent.user.data1 = NULL;
  949. customEvent.user.data2 = NULL;
  950. // A modal loop? Remember what window is "ours"
  951. if (modalPos) modalLoopPos = modalPos;
  952. modalReturn = 0;
  953. ++eventLoopCount;
  954. while (!quit) {
  955. if (grabEvent(&event)) {
  956. int gameMode = (gameLoopH) && (allowDesktopClick) && (currentFocus == NULL);
  957. int storeToGame = 0;
  958. idleTimeout = 0;
  959. didShortTimeout = 0;
  960. // Tooltip? Remove
  961. if ((tooltip) && (event.type != SDL_MOUSEFOCUS)) {
  962. removeWindow(tooltip);
  963. delete tooltip;
  964. tooltip = NULL;
  965. tooltipMs = 0;
  966. }
  967. // Skip user-generated input events EXCEPT key ups/button ups
  968. // (to avoid stuck keys)
  969. // Mouse movement is OK too
  970. if (eventCleanup) {
  971. if ((event.type == SDL_KEYDOWN) ||
  972. (event.type == SDL_MOUSEBUTTONDOWN) ||
  973. (event.type == SDL_MOUSEBUTTONDBL))
  974. continue;
  975. }
  976. // Adjust key modifiers
  977. if ((event.type == SDL_KEYDOWN) || (event.type == SDL_KEYUP)) {
  978. if (gameMode)
  979. // All keys go to game
  980. storeToGame = 1;
  981. else {
  982. // Adjust numeric keypad
  983. if ((event.key.keysym.sym >= SDLK_KP0) && (event.key.keysym.sym <= SDLK_KP9)) {
  984. if (event.key.keysym.mod & KMOD_NUM) {
  985. event.key.keysym.sym = (SDLKey)(event.key.keysym.sym - SDLK_KP0 + SDLK_0);
  986. event.key.keysym.unicode = event.key.keysym.sym;
  987. }
  988. else {
  989. event.key.keysym.sym = keypadConvert[event.key.keysym.sym - SDLK_KP0];
  990. }
  991. }
  992. else if (event.key.keysym.sym == SDLK_KP_PERIOD) {
  993. if (event.key.keysym.mod & KMOD_NUM) {
  994. event.key.keysym.sym = SDLK_PERIOD;
  995. event.key.keysym.unicode = '.';
  996. }
  997. else {
  998. event.key.keysym.sym = SDLK_DELETE;
  999. }
  1000. }
  1001. SDLMod mod = KMOD_NONE;
  1002. if (event.key.keysym.mod & KMOD_ALT) mod = (SDLMod)(mod | KMOD_ALT);
  1003. if (event.key.keysym.mod & KMOD_CTRL) mod = (SDLMod)(mod | KMOD_CTRL);
  1004. if (event.key.keysym.mod & KMOD_SHIFT) mod = (SDLMod)(mod | KMOD_SHIFT);
  1005. event.key.keysym.mod = mod;
  1006. // Do "command release"
  1007. if ((event.type == SDL_KEYUP) && (event.key.keysym.sym == lastShortcutKey) && (lastShortcutKey)) {
  1008. lastShortcutKey = 0;
  1009. customEvent.type = SDL_COMMAND;
  1010. customEvent.user.code = CMD_RELEASE;
  1011. handleEvent(&customEvent);
  1012. customEvent.type = SDL_SPECIAL;
  1013. }
  1014. }
  1015. }
  1016. // Adjust for double-click
  1017. else if (event.type == SDL_MOUSEBUTTONDOWN) {
  1018. if (event.button.button == SDL_BUTTON_LEFT) {
  1019. if ((dclickTicks) &&
  1020. ((SDL_GetTicks() - dclickTicks) < DOUBLECLICK_TIME) &&
  1021. (abs(dclickX - event.button.x) <= DOUBLECLICK_DIST) &&
  1022. (abs(dclickY - event.button.y) <= DOUBLECLICK_DIST)) {
  1023. event.type = SDL_MOUSEBUTTONDBL;
  1024. dclickTicks = 0;
  1025. }
  1026. else {
  1027. dclickX = event.button.x;
  1028. dclickY = event.button.y;
  1029. dclickTicks = SDL_GetTicks();
  1030. }
  1031. }
  1032. else {
  1033. // (cancel upon any non-left click)
  1034. dclickTicks = 0;
  1035. }
  1036. }
  1037. // Special handling for QUIT
  1038. else if ((event.type == SDL_QUIT) || ((event.type == SDL_SPECIAL) && (event.user.code & SDL_SPECIALQUIT))) {
  1039. // Can't quit with modal dialogs open
  1040. if (modalPos) continue;
  1041. // Skip if we're already doing a prequit- that means we have
  1042. // a modal "save" dialog or something open already; this should be
  1043. // covered by the above condition, but "just in case"
  1044. if (preQuit) continue;
  1045. // First, run a pre-quit event
  1046. customEvent.user.code = SDL_PREQUIT;
  1047. preQuit = 1;
  1048. handleEvent(&customEvent);
  1049. preQuit = 0;
  1050. // Cancel the quit?
  1051. if (cancelPreQuit) {
  1052. cancelPreQuit = 0;
  1053. continue;
  1054. }
  1055. if (event.type == SDL_QUIT) quit = 1;
  1056. else quit = event.user.code;
  1057. }
  1058. // Turn resize events into a resolution change
  1059. else if (event.type == SDL_VIDEORESIZE) {
  1060. // Catch non-fatal video errors
  1061. try {
  1062. selectVideoMode(event.resize.w, event.resize.h, -1, 0, 1);
  1063. }
  1064. catch (const VideoException& e) {
  1065. guiErrorBox(string(e.details), errorTitleVideo);
  1066. }
  1067. continue;
  1068. }
  1069. // Turn active events into gain/lose mouse/input focus
  1070. else if (event.type == SDL_ACTIVEEVENT) {
  1071. if ((event.active.state & SDL_APPMOUSEFOCUS) && (event.active.gain == 0) && (currentMouseFocus)) {
  1072. focusEvent(SDL_MOUSEFOCUS, 0, currentMouseFocus);
  1073. currentMouseFocus = NULL;
  1074. selectMouse(MOUSE_NORMAL);
  1075. }
  1076. if ((event.active.state & SDL_APPINPUTFOCUS) && (event.active.gain == 0)) {
  1077. if (!lastFocus) {
  1078. if (previousFocus) lastFocus = previousFocus;
  1079. else lastFocus = currentFocus;
  1080. changeInputFocus(NULL);
  1081. }
  1082. }
  1083. if ((event.active.state & SDL_APPINPUTFOCUS) && (event.active.gain == 1)) {
  1084. // Fixes "stuck ALT" etc
  1085. // @TODO: SDL_ResetKeyboard();
  1086. changeInputFocus(lastFocus);
  1087. lastFocus = NULL;
  1088. // Ignore the mouse click that generated this (hopefully)
  1089. initEventCleanup();
  1090. }
  1091. }
  1092. if (!storeToGame) handleEvent(&event);
  1093. // Tooltips- record time so idle can make them appear
  1094. // Also, COPY all mouse events to game
  1095. if ((event.type == SDL_MOUSEBUTTONDOWN) ||
  1096. (event.type == SDL_MOUSEBUTTONDBL) ||
  1097. (event.type == SDL_MOUSEBUTTONUP) ||
  1098. (event.type == SDL_MOUSEMOTION)) {
  1099. tooltipMs = SDL_GetTicks();
  1100. storeToGame = 1;
  1101. }
  1102. else if ((event.type == SDL_KEYDOWN) ||
  1103. (event.type == SDL_KEYUP) ||
  1104. (event.type == SDL_ACTIVEEVENT)) {
  1105. tooltipMs = 0;
  1106. }
  1107. // Also copy all ACTIVE and QUIT events to game
  1108. if ((event.type == SDL_ACTIVEEVENT) ||
  1109. (event.type == SDL_QUIT)) {
  1110. storeToGame = 1;
  1111. }
  1112. /* SDL_OBJECTCHANGE events no longer use the queue
  1113. **
  1114. // Object change events- delete structure
  1115. else if (event.type == SDL_OBJECTCHANGE) {
  1116. delete (ObjChange*)event.user.data1;
  1117. }
  1118. **
  1119. */
  1120. if ((storeToGame) && (gameMode)) storeEventGame(&event);
  1121. }
  1122. else {
  1123. int phaseChange = 0;
  1124. eventCleanup = 0;
  1125. // Update selection rectangle phase
  1126. Uint32 ticks = SDL_GetTicks();
  1127. // (catch wraparound)
  1128. if (ticks < selectionPhaseMs) selectionPhaseMs = SDL_GetTicks();
  1129. else if (ticks - selectionPhaseMs > DELAY_PHASE_ROTATE) {
  1130. cursorPhase = (cursorPhase + 1) % 64;
  1131. int grey = abs(32 - cursorPhase) << 3;
  1132. if (grey > 255) grey = 255;
  1133. guiRGB[COLOR_TILECURSOR].r = grey;
  1134. guiRGB[COLOR_TILECURSOR].g = grey;
  1135. guiRGB[COLOR_TILECURSOR].b = grey;
  1136. cursorAlpha = abs(16 - cursorPhase % 32) << 4;
  1137. if (cursorAlpha < 64) cursorAlpha = 64;
  1138. if (cursorAlpha > 255) cursorAlpha = 255;
  1139. guiPacked[COLOR_TILECURSOR] = convertRGB(guiRGB[COLOR_TILECURSOR]);
  1140. phaseChange = 1;
  1141. if (cursorPhase % 4 == 0) {
  1142. selectionPhase = (selectionPhase + 1) % NUM_PHASES;
  1143. phaseChange = 2;
  1144. }
  1145. selectionPhaseMs = SDL_GetTicks();
  1146. }
  1147. // Display tooltip?
  1148. if ((tooltipMs) && (!tooltip) && (currentMouseFocus) &&
  1149. ((SDL_GetMouseState(NULL, NULL) & (SDL_BUTTON_LMASK | SDL_BUTTON_RMASK)) == 0)) {
  1150. // (catch wraparound)
  1151. if (ticks < tooltipMs) tooltipMs = SDL_GetTicks();
  1152. else if (ticks - tooltipMs > DELAY_TOOLTIP_SHOW) {
  1153. tooltipMs = 0;
  1154. const char* tiptext = currentMouseFocus->tooltip(lastMouseX - currentMouseFocus->getX(), lastMouseY - currentMouseFocus->getY());
  1155. if (tiptext) {
  1156. tooltip = new ToolTip(tiptext);
  1157. tooltip->popup(lastMouseX, lastMouseY + mouseTooltipYOffset());
  1158. }
  1159. }
  1160. }
  1161. // Idle event
  1162. customEvent.user.code = phaseChange == 2 ? SDL_IDLEPHASE : phaseChange ? SDL_IDLECURSOR : SDL_IDLE;
  1163. // Longer idle timeouts
  1164. ++idleTimeout;
  1165. if (idleTimeout > DELAY_IDLE_LONG) {
  1166. customEvent.user.code = SDL_IDLETIMEOUTLONG;
  1167. idleTimeout = 0;
  1168. }
  1169. else if ((!didShortTimeout) && (idleTimeout > DELAY_IDLE_SHORT)) {
  1170. customEvent.user.code = SDL_IDLETIMEOUTSHORT;
  1171. didShortTimeout = 1;
  1172. }
  1173. handleEvent(&customEvent);
  1174. // @TODO: skip if calling game loop, have game loop delay as usual?
  1175. SDL_Delay(1);
  1176. }
  1177. // Skip screen update if focus/close events waiting
  1178. if (eventsWaiting()) continue;
  1179. updateScreen();
  1180. // If a modal loop, check if our modal dialog has closed- that means we're done
  1181. if (modalLoopPos) {
  1182. if (!verifyWindow(modalLoopPos)) break;
  1183. // Check that our modal window is on top, or a window allowed to sort above it
  1184. // ("no window" is also allowed, because that occurs when you switch to another app)
  1185. if ((modalLoopPos != currentFocus) && (currentFocus) && (currentFocus->windowSort() <= Window::WINDOWSORT_MODAL)) {
  1186. changeInputFocus(modalLoopPos);
  1187. }
  1188. }
  1189. }
  1190. --eventLoopCount;
  1191. // Return value
  1192. int returnValue = 0;
  1193. // End quit state if all loops have ended
  1194. if (eventLoopCount == 0) {
  1195. returnValue = quit;
  1196. quit = 0;
  1197. }
  1198. // Return modal value?
  1199. if (modalReturn) {
  1200. // (We do have to reset it to zero again)
  1201. returnValue = modalReturn;
  1202. modalReturn = 0;
  1203. }
  1204. return returnValue;
  1205. }
  1206. void Desktop::closeAllWindows() { start_func
  1207. SDL_Event customEvent;
  1208. customEvent.type = SDL_CLOSE;
  1209. customEvent.user.code = 0;
  1210. customEvent.user.data1 = NULL;
  1211. customEvent.user.data2 = NULL;
  1212. list<Window*>::iterator pos;
  1213. // Reverse order
  1214. for (pos = windowDataFocus.begin(); pos != windowDataFocus.end(); pos = windowDataFocus.begin()) {
  1215. assert(*pos);
  1216. Window* ptr = *pos;
  1217. windowRunEvent(ptr, &customEvent);
  1218. // In case it hasn't removed itself
  1219. removeWindow(ptr);
  1220. if (ptr->wantsToBeDeleted()) {
  1221. ptr->setParent(NULL);
  1222. delete ptr;
  1223. }
  1224. }
  1225. }
  1226. void Desktop::deleteAllWindows() { start_func
  1227. currentFocus = NULL;
  1228. previousFocus = NULL;
  1229. lastFocus = NULL;
  1230. currentMouseFocus = NULL;
  1231. modalPos = NULL;
  1232. // Note that we purposely use windowDataFocus here
  1233. // and windowDataDisplay in removeWindow
  1234. windowDataDisplay.clear();
  1235. list<Window*>::reverse_iterator pos;
  1236. list<Window*>::reverse_iterator next;
  1237. list<Window*>::reverse_iterator end = windowDataFocus.rend();
  1238. // Reverse order
  1239. for (pos = windowDataFocus.rbegin(); pos != end; pos = next) {
  1240. assert(*pos);
  1241. // Note the next element in sequence now, before we delete
  1242. next = pos;
  1243. ++next;
  1244. // Delete- removeWindow, if called, shouldn't do anything
  1245. (*pos)->setParent(NULL);
  1246. if ((*pos)->wantsToBeDeleted()) delete *pos;
  1247. }
  1248. windowDataFocus.clear();
  1249. }
  1250. void Desktop::broadcastObjChange(int code, void* object, int data1, int data2, Window* exempt) { start_func
  1251. ObjChange obj;
  1252. obj.obj = object;
  1253. obj.info1 = data1;
  1254. obj.info2 = data2;
  1255. SDL_Event customEvent;
  1256. customEvent.type = SDL_OBJECTCHANGE;
  1257. customEvent.user.code = code;
  1258. customEvent.user.data1 = &obj;
  1259. customEvent.user.data2 = exempt;
  1260. /* SDL_OBJECTCHANGE events no longer use the queue
  1261. **
  1262. // In order to use this, obj (above) needs to be dynamically allocated again
  1263. // combinatoryEvent(&customEvent);
  1264. **
  1265. */
  1266. handleEvent(&customEvent);
  1267. }
  1268. int Desktop::broadcastEvent(int type, int code, void* data, Window* exempt, int runImmediate) { start_func
  1269. assert((type == SDL_COMMAND) || (type == SDL_SPECIAL) || (type == SDL_SYSKEY) ||
  1270. (type == SDL_QUIT));
  1271. assert(type < SDL_NUMEVENTS);
  1272. SDL_Event customEvent;
  1273. customEvent.type = type;
  1274. customEvent.user.code = code;
  1275. customEvent.user.data1 = data;
  1276. customEvent.user.data2 = exempt;
  1277. if (runImmediate) {
  1278. return handleEvent(&customEvent);
  1279. }
  1280. else if (SDL_PushEvent(&customEvent)) {
  1281. fatalCrash(0, "SDL_PushEvent failure");
  1282. }
  1283. return 0;
  1284. }
  1285. void Desktop::focusEvent(int type, int code, Window* window) { start_func
  1286. if (!window) return;
  1287. assert((type == SDL_MOUSEFOCUS) || (type == SDL_CLOSE) || (type == SDL_INPUTFOCUS));
  1288. assert((code == 0) || (code == 1));
  1289. SDL_Event customEvent;
  1290. customEvent.type = type;
  1291. customEvent.user.code = code;
  1292. customEvent.user.data1 = window;
  1293. customEvent.user.data2 = NULL;
  1294. if (type == SDL_CLOSE) preemptEvent(&customEvent);
  1295. else insertEvent(&customEvent);
  1296. }
  1297. void Desktop::setModalReturn(int returnValue) { start_func
  1298. if (modalPos) modalReturn = returnValue;
  1299. }
  1300. void Desktop::cancelQuit() { start_func
  1301. if (preQuit) cancelPreQuit = 1;
  1302. }
  1303. int Desktop::cancelQuitState() { start_func
  1304. return cancelPreQuit;
  1305. }
  1306. void Desktop::resolutionChange(int fromW, int fromH, int fromBpp, int toW, int toH, int toBpp) { start_func
  1307. overallWidth = screenWidth;
  1308. overallHeight = screenHeight;
  1309. setBackground(backgroundImage, backgroundColorIndex);
  1310. WindowCollection::resolutionChange(fromW, fromH, fromBpp, toW, toH, toBpp);
  1311. }
  1312. void Desktop::setChildDirty() { start_func
  1313. childDirty = 1;
  1314. }
  1315. void Desktop::setBackgroundAlpha(int alpha) { start_func
  1316. if (alpha > 255) alpha = 255;
  1317. if (alpha < 0) alpha = 0;
  1318. backgroundAlpha = alpha;
  1319. updateRect.x = 0;
  1320. updateRect.y = 0;
  1321. updateRect.w = overallWidth;
  1322. updateRect.h = overallHeight;
  1323. }
  1324. void Desktop::setBackground(SDL_Surface* image, int colorIndex) { start_func
  1325. assert(colorIndex >= 0);
  1326. assert(colorIndex < COLOR_COUNT);
  1327. backgroundColorIndex = colorIndex;
  1328. if (image) {
  1329. // Calculate position (centered)
  1330. bkSrcW = image->w;
  1331. bkSrcH = image->h;
  1332. bkSrcX = 0;
  1333. bkSrcY = 0;
  1334. bkDestX = (overallWidth - bkSrcW) / 2;
  1335. bkDestY = (overallHeight - bkSrcH) / 2;
  1336. if (bkDestX < 0) {
  1337. bkSrcW = overallWidth;
  1338. bkSrcX = -bkDestX;
  1339. bkDestX = 0;
  1340. }
  1341. if (bkDestY < 0) {
  1342. bkSrcH = overallHeight;
  1343. bkSrcY = -bkDestY;
  1344. bkDestY = 0;
  1345. }
  1346. SDL_Surface* convertedBk = SDL_ConvertSurface(image, getScreen()->format, getScreen()->flags);
  1347. if (backgroundImage) SDL_FreeSurface(backgroundImage);
  1348. backgroundImage = convertedBk;
  1349. }
  1350. else {
  1351. if (backgroundImage) SDL_FreeSurface(backgroundImage);
  1352. backgroundImage = NULL;
  1353. }
  1354. updateRect.x = 0;
  1355. updateRect.y = 0;
  1356. updateRect.w = overallWidth;
  1357. updateRect.h = overallHeight;
  1358. }
  1359. void Desktop::initEventCleanup() { start_func
  1360. eventCleanup = 1;
  1361. }
  1362. Desktop::Desktop() : WindowCollection() { start_func
  1363. gameLoopH = NULL;
  1364. supportsCommandH = NULL;
  1365. backgroundImage = NULL;
  1366. backgroundColorIndex = COLOR_FRONTDESKTOP;
  1367. backgroundAlpha = 255;
  1368. quit = 0;
  1369. preQuit = 0;
  1370. cancelPreQuit = 0;
  1371. eventLoopCount = 0;
  1372. modalReturn = 0;
  1373. eventCleanup = 0;
  1374. overallWidth = screenWidth;
  1375. overallHeight = screenHeight;
  1376. canvasWidth = screenWidth;
  1377. canvasHeight = screenHeight;
  1378. updateRect.x = 0;
  1379. updateRect.y = 0;
  1380. updateRect.w = overallWidth;
  1381. updateRect.h = overallHeight;
  1382. childDirty = 0;
  1383. dclickTicks = 0;
  1384. selectionPhase = 0;
  1385. selectionPhaseMs = SDL_GetTicks();
  1386. cursorPhase = 0;
  1387. cursorAlpha = 128;
  1388. tooltip = NULL;
  1389. tooltipMs = 0;
  1390. lastShortcutKey = 0;
  1391. }
  1392. Desktop::~Desktop() { start_func
  1393. if (backgroundImage) SDL_FreeSurface(backgroundImage);
  1394. }
  1395. int Desktop::getScreenX() const { start_func
  1396. return 0;
  1397. }
  1398. int Desktop::getScreenY() const { start_func
  1399. return 0;
  1400. }
  1401. int Desktop::desktopX() const { start_func
  1402. return canvasX;
  1403. }
  1404. int Desktop::desktopY() const { start_func
  1405. return canvasY;
  1406. }
  1407. int Desktop::desktopWidth() const { start_func
  1408. return canvasWidth;
  1409. }
  1410. int Desktop::desktopHeight() const { start_func
  1411. return canvasHeight;
  1412. }
  1413. void Desktop::childMoved(int fromX, int fromY, int toX, int toY, Window* child) { start_func
  1414. assert(child);
  1415. Rect moved;
  1416. child->getRect(moved);
  1417. addToUpdateRect(moved);
  1418. moved.x = fromX;
  1419. moved.y = fromY;
  1420. addToUpdateRect(moved);
  1421. }
  1422. void Desktop::childResized(int fromW, int fromH, int toW, int toH, Window* child) { start_func
  1423. assert(child);
  1424. Rect moved;
  1425. child->getRect(moved);
  1426. if (fromW > toW) moved.w = fromW;
  1427. if (fromH > toH) moved.h = fromH;
  1428. addToUpdateRect(moved);
  1429. }
  1430. void Desktop::childDeleted(Window* child) { start_func
  1431. assert(child);
  1432. // (just in case the window forgot to remove itself from the desktop)
  1433. removeWindow(child);
  1434. }
  1435. void Desktop::childModified(Window* child) { start_func
  1436. assert(child);
  1437. }
  1438. void Desktop::siblingModified(Window* sibling) { start_func
  1439. assert(sibling);
  1440. }