Annotation of brogue-ce/src/platform/sdl2-platform.c, Revision 1.1.1.1
1.1 rubenllo 1: #ifdef SDL_PATHS
2: #include <unistd.h>
3: #endif
4:
5: #include <limits.h>
6: #include <SDL_image.h>
7: #include "platform.h"
8: #include "tiles.h"
9:
10: #define PAUSE_BETWEEN_EVENT_POLLING 36L//17
11: #define MAX_REMAPS 128
12:
13: struct keypair {
14: char from;
15: char to;
16: };
17:
18: static struct keypair remapping[MAX_REMAPS];
19: static size_t nremaps = 0;
20: static boolean showGraphics = false;
21:
22: static rogueEvent lastEvent;
23:
24:
25: static void sdlfatal(char *file, int line) {
26: fprintf(stderr, "Fatal SDL error (%s:%d): %s\n", file, line, SDL_GetError());
27: exit(1);
28: }
29:
30:
31: static void imgfatal(char *file, int line) {
32: fprintf(stderr, "Fatal SDL_image error (%s:%d): %s\n", file, line, IMG_GetError());
33: exit(1);
34: }
35:
36:
37: /*
38: If the key is to be processed, returns true and updates event. False
39: otherwise. This function only listens for keypresses which do not produce
40: corresponding TextInputEvents.
41: */
42: static boolean eventFromKey(rogueEvent *event, SDL_Keycode key) {
43: event->param1 = -1;
44:
45: switch (key) {
46: case SDLK_ESCAPE:
47: event->param1 = ESCAPE_KEY;
48: return true;
49: case SDLK_UP:
50: event->param1 = UP_ARROW;
51: return true;
52: case SDLK_DOWN:
53: event->param1 = DOWN_ARROW;
54: return true;
55: case SDLK_RIGHT:
56: event->param1 = RIGHT_ARROW;
57: return true;
58: case SDLK_LEFT:
59: event->param1 = LEFT_ARROW;
60: return true;
61: case SDLK_RETURN:
62: case SDLK_KP_ENTER:
63: event->param1 = RETURN_KEY;
64: return true;
65: case SDLK_BACKSPACE:
66: event->param1 = DELETE_KEY;
67: return true;
68: case SDLK_TAB:
69: event->param1 = TAB_KEY;
70: return true;
71: case SDLK_PRINTSCREEN:
72: event->param1 = PRINTSCREEN_KEY;
73: return true;
74: }
75:
76: /*
77: Only process keypad events when we're holding a modifier, as there is no
78: TextInputEvent then.
79: */
80: if (event->shiftKey || event->controlKey) {
81: switch (key) {
82: case SDLK_KP_0:
83: event->param1 = NUMPAD_0;
84: return true;
85: case SDLK_KP_1:
86: event->param1 = NUMPAD_1;
87: return true;
88: case SDLK_KP_2:
89: event->param1 = NUMPAD_2;
90: return true;
91: case SDLK_KP_3:
92: event->param1 = NUMPAD_3;
93: return true;
94: case SDLK_KP_4:
95: event->param1 = NUMPAD_4;
96: return true;
97: case SDLK_KP_5:
98: event->param1 = NUMPAD_5;
99: return true;
100: case SDLK_KP_6:
101: event->param1 = NUMPAD_6;
102: return true;
103: case SDLK_KP_7:
104: event->param1 = NUMPAD_7;
105: return true;
106: case SDLK_KP_8:
107: event->param1 = NUMPAD_8;
108: return true;
109: case SDLK_KP_9:
110: event->param1 = NUMPAD_9;
111: return true;
112: }
113: }
114:
115: // Ctrl+letter doesn't give a TextInputEvent
116: if (event->controlKey && key >= SDLK_a && key <= SDLK_z) {
117: event->param1 = 'a' + (key - SDLK_a);
118: if (event->shiftKey) event->param1 -= 'a' - 'A';
119: return true;
120: }
121:
122: return false;
123: }
124:
125:
126: static boolean _modifierHeld(int mod) {
127: SDL_Keymod km = SDL_GetModState();
128: return mod == 0 && (km & (KMOD_LSHIFT | KMOD_RSHIFT))
129: || mod == 1 && (km & (KMOD_LCTRL | KMOD_RCTRL));
130: }
131:
132:
133: static char applyRemaps(char c) {
134: for (size_t i=0; i < nremaps; i++) {
135: if (remapping[i].from == c) return remapping[i].to;
136: }
137: return c;
138: }
139:
140:
141: /*
142: If an event is available, returns true and updates returnEvent. Otherwise
143: it returns false and an error event. This function also processes
144: platform-specific inputs/behaviours.
145: */
146: static boolean pollBrogueEvent(rogueEvent *returnEvent, boolean textInput) {
147: static int mx = 0, my = 0;
148:
149: returnEvent->eventType = EVENT_ERROR;
150: returnEvent->shiftKey = _modifierHeld(0);
151: returnEvent->controlKey = _modifierHeld(1);
152:
153: SDL_Event event;
154: boolean ret = false;
155:
156:
157: // ~ for (int i=0; i < 100 && SDL_PollEvent(&event); i++) {
158: while (SDL_PollEvent(&event)) {
159: if (event.type == SDL_QUIT) {
160: // the player clicked the X button!
161: SDL_Quit();
162: quitImmediately();
163: } else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
164: resizeWindow(event.window.data1, event.window.data2);
165: } else if (event.type == SDL_KEYDOWN) {
166: SDL_Keycode key = event.key.keysym.sym;
167:
168: if (key == SDLK_PAGEUP) {
169: resizeWindow(max(windowWidth * 11/10, windowWidth + 1), max(windowHeight * 11/10, windowHeight + 1));
170: continue;
171: } else if (key == SDLK_PAGEDOWN) {
172: fullScreen = false;
173: resizeWindow(max(windowWidth * 10/11, 1), max(windowHeight * 10/11, 1));
174: continue;
175: } else if (key == SDLK_F11 || key == SDLK_F12
176: || key == SDLK_RETURN && (SDL_GetModState() & KMOD_ALT)) {
177: fullScreen = !fullScreen;
178: resizeWindow(-1, -1); // Reset to starting resolution
179: continue;
180: }
181:
182: if (eventFromKey(returnEvent, key)) {
183: returnEvent->eventType = KEYSTROKE;
184: return true;
185: }
186: } else if (event.type == SDL_TEXTINPUT && (unsigned char)(event.text.text[0]) < 0x80) {
187: /*
188: It's difficult/impossible to check what characters are on the
189: shifts of keys. So to detect '&', '>' etc. reliably we need to
190: listen for text input events as well as keydowns. This results
191: in hybrid keyboard code, where Brogue KEYSTROKEs can come from
192: different SDL events.
193: */
194: char c = event.text.text[0];
195:
196: if (!textInput) {
197: c = applyRemaps(c);
198: if (c == '=' || c == '+') {
199: resizeWindow(max(windowWidth * 11/10, windowWidth + 1), max(windowHeight * 11/10, windowHeight + 1));
200: } else if (c == '-') {
201: fullScreen = false;
202: resizeWindow(max(windowWidth * 10/11, 1), max(windowHeight * 10/11, 1));
203: }
204: }
205:
206: returnEvent->eventType = KEYSTROKE;
207: returnEvent->param1 = c;
208: // ~ printf("textinput %s\n", event.text.text);
209: return true;
210: } else if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) {
211: if (event.button.button == SDL_BUTTON_LEFT || event.button.button == SDL_BUTTON_RIGHT) {
212: if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT) {
213: returnEvent->eventType = MOUSE_DOWN;
214: } else if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT) {
215: returnEvent->eventType = MOUSE_UP;
216: } else if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_RIGHT) {
217: returnEvent->eventType = RIGHT_MOUSE_DOWN;
218: } else if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_RIGHT) {
219: returnEvent->eventType = RIGHT_MOUSE_UP;
220: }
221: returnEvent->param1 = event.button.x * COLS / windowWidth;
222: returnEvent->param2 = event.button.y * ROWS / windowHeight;
223: return true;
224: }
225: } else if (event.type == SDL_MOUSEMOTION) {
226: // We don't want to return on a mouse motion event, because only the last
227: // in the queue is important. That's why we just set ret=true
228: int xcell = event.motion.x * COLS / windowWidth,
229: ycell = event.motion.y * ROWS / windowHeight;
230: if (xcell != mx || ycell != my) {
231: returnEvent->eventType = MOUSE_ENTERED_CELL;
232: returnEvent->param1 = xcell;
233: returnEvent->param2 = ycell;
234: mx = xcell;
235: my = ycell;
236: ret = true;
237: }
238: }
239: }
240:
241: return ret;
242: }
243:
244:
245: static void _gameLoop() {
246: #ifdef SDL_PATHS
247: char *path = SDL_GetBasePath();
248: if (path) {
249: path[strlen(path) - 1] = '\0'; // remove trailing separator
250: strcpy(dataDirectory, path);
251: } else {
252: fprintf(stderr, "Failed to find the path to the application\n");
253: exit(1);
254: }
255: free(path);
256:
257: path = SDL_GetPrefPath("Brogue", "Brogue CE");
258: if (!path || chdir(path) != 0) {
259: fprintf(stderr, "Failed to find or change to the save directory\n");
260: exit(1);
261: }
262: free(path);
263: #endif
264:
265: if (SDL_Init(SDL_INIT_VIDEO) < 0) sdlfatal(__FILE__, __LINE__);
266:
267: if (!(IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG)) imgfatal(__FILE__, __LINE__);
268:
269: initTiles();
270:
271: lastEvent.eventType = EVENT_ERROR;
272:
273: resizeWindow(windowWidth, windowHeight);
274:
275: rogueMain();
276:
277: SDL_Quit();
278: }
279:
280:
281: static boolean _pauseForMilliseconds(short ms) {
282: updateScreen();
283: SDL_Delay(ms);
284:
285: if (lastEvent.eventType != EVENT_ERROR
286: && lastEvent.eventType != MOUSE_ENTERED_CELL) {
287: return true; // SDL already gave us an interrupting event to process
288: }
289:
290: return pollBrogueEvent(&lastEvent, false) // ask SDL for a new event if one is available
291: && lastEvent.eventType != EVENT_ERROR // and check if it is interrupting
292: && lastEvent.eventType != MOUSE_ENTERED_CELL;
293: }
294:
295:
296: static void _nextKeyOrMouseEvent(rogueEvent *returnEvent, boolean textInput, boolean colorsDance) {
297: long tstart, dt;
298:
299: updateScreen();
300:
301: if (lastEvent.eventType != EVENT_ERROR) {
302: *returnEvent = lastEvent;
303: lastEvent.eventType = EVENT_ERROR;
304: return;
305: }
306:
307: while (true) {
308: tstart = SDL_GetTicks();
309:
310: if (colorsDance) {
311: shuffleTerrainColors(3, true);
312: commitDraws();
313: }
314:
315: updateScreen();
316:
317: if (pollBrogueEvent(returnEvent, textInput)) break;
318:
319: dt = PAUSE_BETWEEN_EVENT_POLLING - (SDL_GetTicks() - tstart);
320: if (dt > 0) {
321: SDL_Delay(dt);
322: }
323: }
324: }
325:
326:
327: /*
328: Returns the index of the sprite representing the given glyph. Sprites <256 are
329: from the text font sheet, 256+ are from the tiles sheet.
330: */
331: static int fontIndex(enum displayGlyph glyph) {
332: // These are the only non-ASCII glyphs which always come from the font sheet
333: if (glyph == G_UP_ARROW) return 0x90;
334: if (glyph == G_DOWN_ARROW) return 0x91;
335:
336: if (glyph < 128) {
337: // ASCII characters map directly
338: return glyph;
339: } else if (showGraphics && glyph >= 128) {
340: // Tile glyphs have sprite indices starting at 256
341: // -2 to disregard the up and down arrow glyphs
342: return glyph + 128 - 2;
343: } else {
344: unsigned int code = glyphToUnicode(glyph);
345: switch (code) {
346: case U_MIDDLE_DOT: return 0x80;
347: case U_FOUR_DOTS: return 0x81;
348: case U_DIAMOND: return 0x82;
349: case U_FLIPPED_V: return 0x83;
350: case U_ARIES: return 0x84;
351: case U_ESZETT: return 0xdf;
352: case U_ANKH: return 0x85;
353: case U_MUSIC_NOTE: return 0x86;
354: case U_CIRCLE: return 0x87;
355: case U_LIGHTNING_BOLT: return 0x99;
356: case U_FILLED_CIRCLE: return 0x89;
357: case U_NEUTER: return 0x8a;
358: case U_U_ACUTE: return 0xda;
359: case U_CURRENCY: return 0xa4;
360: case U_UP_ARROW: return 0x90;
361: case U_DOWN_ARROW: return 0x91;
362: case U_LEFT_ARROW: return 0x92;
363: case U_RIGHT_ARROW: return 0x93;
364: case U_OMEGA: return 0x96;
365: case U_CIRCLE_BARS: return 0x8c;
366: case U_FILLED_CIRCLE_BARS: return 0x8d;
367:
368: default:
369: brogueAssert(code < 256);
370: return code;
371: }
372: }
373: }
374:
375:
376: static void _plotChar(
377: enum displayGlyph inputChar,
378: short x, short y,
379: short foreRed, short foreGreen, short foreBlue,
380: short backRed, short backGreen, short backBlue
381: ) {
382: updateTile(y, x, fontIndex(inputChar),
383: foreRed, foreGreen, foreBlue,
384: backRed, backGreen, backBlue);
385: }
386:
387:
388: static void _remap(const char *from, const char *to) {
389: if (nremaps < MAX_REMAPS) {
390: remapping[nremaps].from = from[0];
391: remapping[nremaps].to = to[0];
392: nremaps++;
393: }
394: }
395:
396:
397: /*
398: * Take screenshot in current working directory (ScreenshotN.png)
399: */
400: static boolean _takeScreenshot() {
401: SDL_Surface *screenshot = captureScreen();
402: if (!screenshot) return false;
403:
404: // choose filename
405: char screenshotFilepath[BROGUE_FILENAME_MAX];
406: getAvailableFilePath(screenshotFilepath, "Screenshot", SCREENSHOT_SUFFIX);
407: strcat(screenshotFilepath, SCREENSHOT_SUFFIX);
408:
409: // save to PNG
410: IMG_SavePNG(screenshot, screenshotFilepath);
411: SDL_FreeSurface(screenshot);
412:
413: return true;
414: }
415:
416:
417: static boolean _setGraphicsEnabled(boolean state) {
418: showGraphics = state;
419: refreshScreen();
420: updateScreen();
421: return state;
422: }
423:
424:
425: struct brogueConsole sdlConsole = {
426: _gameLoop,
427: _pauseForMilliseconds,
428: _nextKeyOrMouseEvent,
429: _plotChar,
430: _remap,
431: _modifierHeld,
432: NULL,
433: _takeScreenshot,
434: _setGraphicsEnabled
435: };
CVSweb