1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software{} you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation{} either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY{} without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program{} if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "glk/glk_api.h"
24 #include "glk/conf.h"
25 #include "glk/events.h"
26 #include "glk/picture.h"
27 #include "glk/sound.h"
28 #include "glk/streams.h"
29 #include "glk/unicode.h"
30 #include "glk/windows.h"
31 #include "glk/window_graphics.h"
32 #include "glk/window_text_buffer.h"
33 #include "glk/window_pair.h"
34 #include "common/translation.h"
35 
36 namespace Glk {
37 
GlkAPI(OSystem * syst,const GlkGameDescription & gameDesc)38 GlkAPI::GlkAPI(OSystem *syst, const GlkGameDescription &gameDesc) :
39 		GlkEngine(syst, gameDesc), _gliFirstEvent(false) {
40 	// Set uppercase/lowercase tables
41 	int ix, res;
42 	for (ix = 0; ix < 256; ix++) {
43 		_charToupperTable[ix] = ix;
44 		_charTolowerTable[ix] = ix;
45 	}
46 
47 	for (ix = 0; ix < 256; ix++) {
48 		if (ix >= 'A' && ix <= 'Z')
49 			res = ix + ('a' - 'A');
50 		else if (ix >= 0xC0 && ix <= 0xDE && ix != 0xD7)
51 			res = ix + 0x20;
52 		else
53 			res = 0;
54 
55 		if (res) {
56 			_charTolowerTable[ix] = res;
57 			_charToupperTable[res] = ix;
58 		}
59 	}
60 }
61 
glk_exit(void)62 void GlkAPI::glk_exit(void) {
63 	glk_put_string(_("[ press any key to exit ]"));
64 	_events->waitForPress();
65 
66 	// Trigger a ScumMVM shutdown of game
67 	quitGame();
68 	Common::Event e;
69 	g_system->getEventManager()->pollEvent(e);
70 }
71 
glk_set_interrupt_handler(void (* func)(void))72 void GlkAPI::glk_set_interrupt_handler(void(*func)(void)) {
73 	// This library doesn't handle interrupts.
74 }
75 
glk_tick(void)76 void GlkAPI::glk_tick(void) {
77 	// Nothing needed
78 }
79 
glk_gestalt(uint id,uint val)80 uint GlkAPI::glk_gestalt(uint id, uint val) {
81 	return glk_gestalt_ext(id, val, nullptr, 0);
82 }
83 
glk_gestalt_ext(uint id,uint val,uint * arr,uint arrlen)84 uint GlkAPI::glk_gestalt_ext(uint id, uint val, uint *arr, uint arrlen) {
85 	switch (id) {
86 	case gestalt_Version:
87 		return 0x00000703;
88 
89 	case gestalt_LineInput:
90 		if (val >= 32 && val < 0x10ffff)
91 			return true;
92 		else
93 			return false;
94 
95 	case gestalt_CharInput:
96 		if (val >= 32 && val < 0x10ffff)
97 			return true;
98 		else if (val == keycode_Return)
99 			return true;
100 		else
101 			return false;
102 
103 	case gestalt_CharOutput:
104 		if (val >= 32 && val < 0x10ffff) {
105 			if (arr && arrlen >= 1)
106 				arr[0] = 1;
107 			return gestalt_CharOutput_ExactPrint;
108 		} else {
109 			// cheaply, we don't do any translation of printed characters,
110 			// so the output is always one character even if it's wrong.
111 			if (arr && arrlen >= 1)
112 				arr[0] = 1;
113 			return gestalt_CharOutput_CannotPrint;
114 		}
115 
116 	case gestalt_MouseInput:
117 		if (val == wintype_TextGrid)
118 			return true;
119 		if (val == wintype_Graphics)
120 			return true;
121 		return false;
122 
123 	case gestalt_Graphics:
124 	case gestalt_GraphicsTransparency:
125 		return g_conf->_graphics;
126 
127 	case gestalt_DrawImage:
128 		if (val == wintype_TextBuffer)
129 			return g_conf->_graphics;
130 		if (val == wintype_Graphics)
131 			return g_conf->_graphics;
132 		return false;
133 
134 	case gestalt_Sound:
135 	case gestalt_SoundVolume:
136 	case gestalt_SoundMusic:
137 	case gestalt_SoundNotify:
138 		return g_conf->_sound;
139 
140 	case gestalt_LineTerminatorKey:
141 		return Window::checkBasicTerminators(val);
142 
143 	case gestalt_Timer:
144 	case gestalt_Unicode:
145 	case gestalt_UnicodeNorm:
146 	case gestalt_Hyperlinks:
147 	case gestalt_HyperlinkInput:
148 	case gestalt_LineInputEcho:
149 	case gestalt_LineTerminators:
150 	case gestalt_DateTime:
151 	case gestalt_GarglkText:
152 		return true;
153 
154 	case gestalt_Sound2:
155 	default:
156 		return false;
157 	}
158 }
159 
glk_char_to_lower(unsigned char ch)160 unsigned char GlkAPI::glk_char_to_lower(unsigned char ch) {
161 	return _charTolowerTable[ch];
162 }
163 
glk_char_to_upper(unsigned char ch)164 unsigned char GlkAPI::glk_char_to_upper(unsigned char ch) {
165 	return _charToupperTable[ch];
166 }
167 
glk_window_get_root(void) const168 winid_t GlkAPI::glk_window_get_root(void) const {
169 	return _windows->getRoot();
170 }
171 
glk_window_open(winid_t split,uint method,uint size,uint wintype,uint rock) const172 winid_t GlkAPI::glk_window_open(winid_t split, uint method, uint size, uint wintype, uint rock) const {
173 	return _windows->windowOpen(split, method, size, wintype, rock);
174 }
175 
glk_window_close(winid_t win,stream_result_t * result)176 void GlkAPI::glk_window_close(winid_t win, stream_result_t *result) {
177 	if (win) {
178 		_windows->windowClose(win, result);
179 	} else {
180 		warning("glk_window_close: invalid ref");
181 	}
182 }
183 
glk_window_get_size(winid_t win,uint * width,uint * height)184 void GlkAPI::glk_window_get_size(winid_t win, uint *width, uint *height) {
185 	if (win) {
186 		win->getSize(width, height);
187 	} else {
188 		warning("window_get_size: invalid ref");
189 	}
190 }
191 
glk_window_set_arrangement(winid_t win,uint method,uint size,winid_t keywin)192 void GlkAPI::glk_window_set_arrangement(winid_t win, uint method, uint size, winid_t keywin) {
193 	if (win) {
194 		win->setArrangement(method, size, keywin);
195 	} else {
196 		warning("window_set_arrangement: invalid ref");
197 	}
198 }
199 
glk_window_get_arrangement(winid_t win,uint * method,uint * size,winid_t * keyWin)200 void GlkAPI::glk_window_get_arrangement(winid_t win, uint *method,
201                                      uint *size, winid_t *keyWin) {
202 	if (win) {
203 		win->getArrangement(method, size, keyWin);
204 	} else {
205 		warning("window_get_arrangement: invalid ref");
206 	}
207 }
208 
glk_window_iterate(winid_t win,uint * rock)209 winid_t GlkAPI::glk_window_iterate(winid_t win, uint *rock) {
210 	win = win ? win->_next : _windows->getRoot();
211 
212 	if (win) {
213 		if (rock)
214 			*rock = win->_rock;
215 		return win;
216 	}
217 
218 	if (rock)
219 		*rock = 0;
220 
221 	return nullptr;
222 }
223 
glk_window_get_rock(winid_t win)224 uint GlkAPI::glk_window_get_rock(winid_t win) {
225 	if (win) {
226 		return win->_rock;
227 	} else {
228 		warning("window_get_rock: invalid ref.");
229 		return 0;
230 	}
231 }
232 
glk_window_get_type(winid_t win)233 uint GlkAPI::glk_window_get_type(winid_t win) {
234 	if (win) {
235 		return win->_type;
236 	} else {
237 		warning("window_get_parent: invalid ref");
238 		return 0;
239 	}
240 }
241 
glk_window_get_parent(winid_t win)242 winid_t GlkAPI::glk_window_get_parent(winid_t win) {
243 	if (!win) {
244 		warning("window_get_parent: invalid ref");
245 		return 0;
246 	}
247 
248 	return win->_parent;
249 }
250 
glk_window_get_sibling(winid_t win)251 winid_t GlkAPI::glk_window_get_sibling(winid_t win) {
252 	if (!win) {
253 		warning("window_get_sibling: invalid ref");
254 		return nullptr;
255 	}
256 
257 	PairWindow *parentWin = dynamic_cast<PairWindow *>(win->_parent);
258 	if (!parentWin)
259 		return nullptr;
260 
261 	int index = parentWin->_children.indexOf(win);
262 	if (index == ((int)parentWin->_children.size() - 1))
263 		return parentWin->_children.front();
264 	else if (index >= 0)
265 		return parentWin->_children[index + 1];
266 
267 	return nullptr;
268 }
269 
glk_window_clear(winid_t win)270 void GlkAPI::glk_window_clear(winid_t win) {
271 	if (!win) {
272 		warning("window_clear: invalid ref");
273 	} else {
274 		if (win->_lineRequest || win->_lineRequestUni) {
275 			if (g_conf->_safeClicks && _events->_forceClick) {
276 				glk_cancel_line_event(win, nullptr);
277 				_events->_forceClick = false;
278 
279 				win->clear();
280 			} else {
281 				warning("window_clear: window has pending line request");
282 				return;
283 			}
284 		}
285 
286 		// Clear the window
287 		win->clear();
288 	}
289 }
290 
glk_window_move_cursor(winid_t win,uint xpos,uint ypos)291 void GlkAPI::glk_window_move_cursor(winid_t win, uint xpos, uint ypos) {
292 	if (win) {
293 		win->moveCursor(Point(xpos, ypos));
294 	} else {
295 		warning("window_move_cursor: invalid ref");
296 	}
297 }
298 
glk_window_get_stream(winid_t win)299 strid_t GlkAPI::glk_window_get_stream(winid_t win) {
300 	if (win) {
301 		return win->_stream;
302 	} else {
303 		warning("window_get_stream: invalid ref");
304 		return nullptr;
305 	}
306 }
307 
glk_window_set_echo_stream(winid_t win,strid_t str)308 void GlkAPI::glk_window_set_echo_stream(winid_t win, strid_t str) {
309 	if (win) {
310 		win->_echoStream = str;
311 	} else {
312 		warning("window_set_echo_stream: invalid window id");
313 	}
314 }
315 
glk_window_get_echo_stream(winid_t win)316 strid_t GlkAPI::glk_window_get_echo_stream(winid_t win) {
317 	if (!win) {
318 		warning("window_get_echo_stream: invalid ref");
319 		return nullptr;
320 	}
321 
322 	return win->_echoStream;
323 }
324 
glk_set_window(winid_t win)325 void GlkAPI::glk_set_window(winid_t win) {
326 	_streams->setCurrent(win ? win->_stream : nullptr);
327 }
328 
glk_stream_open_file(frefid_t fileref,FileMode fmode,uint rock)329 strid_t GlkAPI::glk_stream_open_file(frefid_t fileref, FileMode fmode, uint rock) {
330 	return _streams->openFileStream(fileref, fmode, rock, false);
331 }
332 
glk_stream_open_memory(char * buf,uint buflen,FileMode fmode,uint rock)333 strid_t GlkAPI::glk_stream_open_memory(char *buf, uint buflen, FileMode fmode, uint rock) {
334 	return _streams->openMemoryStream(buf, buflen, fmode, rock, false);
335 }
336 
glk_stream_close(strid_t str,stream_result_t * result)337 void GlkAPI::glk_stream_close(strid_t str, stream_result_t *result) {
338 	str->close(result);
339 }
340 
glk_stream_iterate(strid_t str,uint * rockptr) const341 strid_t GlkAPI::glk_stream_iterate(strid_t str, uint *rockptr) const {
342 	return str ? str->getNext(rockptr) : _streams->getFirst(rockptr);
343 }
344 
glk_stream_get_rock(strid_t str) const345 uint GlkAPI::glk_stream_get_rock(strid_t str) const {
346 	if (!str) {
347 		warning("stream_get_rock: invalid ref");
348 		return 0;
349 	}
350 
351 	return str->getRock();
352 }
353 
glk_stream_set_position(strid_t str,int pos,uint seekMode)354 void GlkAPI::glk_stream_set_position(strid_t str, int pos, uint seekMode) {
355 	if (str) {
356 		str->setPosition(pos, seekMode);
357 	} else {
358 		warning("stream_set_position: invalid ref");
359 	}
360 }
361 
glk_stream_get_position(strid_t str) const362 uint GlkAPI::glk_stream_get_position(strid_t str) const {
363 	if (str) {
364 		return str->getPosition();
365 	} else {
366 		warning("stream_get_position: invalid ref");
367 		return 0;
368 	}
369 }
370 
glk_stream_set_current(strid_t str)371 void GlkAPI::glk_stream_set_current(strid_t str) {
372 	_streams->setCurrent(str);
373 }
374 
glk_stream_get_current(void)375 strid_t GlkAPI::glk_stream_get_current(void) {
376 	return _streams->getCurrent();
377 }
378 
glk_put_char(unsigned char ch)379 void GlkAPI::glk_put_char(unsigned char ch) {
380 	_streams->getCurrent()->putChar(ch);
381 }
382 
glk_put_char_stream(strid_t str,unsigned char ch)383 void GlkAPI::glk_put_char_stream(strid_t str, unsigned char ch) {
384 	if (str) {
385 		str->putChar(ch);
386 	} else {
387 		warning("put_char_stream: invalid ref");
388 	}
389 }
390 
glk_put_string(const char * s)391 void GlkAPI::glk_put_string(const char *s) {
392 	_streams->getCurrent()->putBuffer(s, strlen(s));
393 }
394 
glk_put_string_stream(strid_t str,const char * s)395 void GlkAPI::glk_put_string_stream(strid_t str, const char *s) {
396 	str->putBuffer(s, strlen(s));
397 }
398 
glk_put_buffer(const char * buf,uint len)399 void GlkAPI::glk_put_buffer(const char *buf, uint len) {
400 	_streams->getCurrent()->putBuffer(buf, len);
401 }
402 
glk_put_buffer_stream(strid_t str,const char * buf,uint len)403 void GlkAPI::glk_put_buffer_stream(strid_t str, const char *buf, uint len) {
404 	str->putBuffer(buf, len);
405 }
406 
glk_set_style(uint styl)407 void GlkAPI::glk_set_style(uint styl) {
408 	_streams->getCurrent()->setStyle(styl);
409 }
410 
glk_set_style_stream(strid_t str,uint styl)411 void GlkAPI::glk_set_style_stream(strid_t str, uint styl) {
412 	if (str) {
413 		str->setStyle(styl);
414 	} else {
415 		warning("set_style_stream: invalid ref");
416 	}
417 }
418 
glk_get_char_stream(strid_t str)419 int GlkAPI::glk_get_char_stream(strid_t str) {
420 	if (str) {
421 		return str->getChar();
422 	} else {
423 		warning("get_char_stream: invalid ref");
424 		return -1;
425 	}
426 }
427 
glk_get_line_stream(strid_t str,char * buf,uint len)428 uint GlkAPI::glk_get_line_stream(strid_t str, char *buf, uint len) {
429 	if (str) {
430 		return str->getLine(buf, len);
431 	} else {
432 		warning("get_line_stream: invalid ref");
433 		return 0;
434 	}
435 }
436 
glk_get_buffer_stream(strid_t str,char * buf,uint len)437 uint GlkAPI::glk_get_buffer_stream(strid_t str, char *buf, uint len) {
438 	if (str) {
439 		return str->getBuffer(buf, len);
440 	} else {
441 		warning("get_line_stream: invalid ref");
442 		return 0;
443 	}
444 }
445 
glk_stylehint_set(uint wintype,uint style,uint hint,int val)446 void GlkAPI::glk_stylehint_set(uint wintype, uint style, uint hint, int val) {
447 	WindowStyle *styles;
448 	bool p, b, i;
449 
450 	if (wintype == wintype_AllTypes) {
451 		glk_stylehint_set(wintype_TextGrid, style, hint, val);
452 		glk_stylehint_set(wintype_TextBuffer, style, hint, val);
453 		return;
454 	}
455 
456 	if (wintype == wintype_TextGrid)
457 		styles = g_conf->_gStyles;
458 	else if (wintype == wintype_TextBuffer)
459 		styles = g_conf->_tStyles;
460 	else
461 		return;
462 
463 	if (!g_conf->_styleHint)
464 		return;
465 
466 	switch (hint) {
467 	case stylehint_TextColor:
468 		styles[style].fg = val;
469 		break;
470 
471 	case stylehint_BackColor:
472 		styles[style].bg = val;
473 		break;
474 
475 	case stylehint_ReverseColor:
476 		styles[style].reverse = (val != 0);
477 		break;
478 
479 	case stylehint_Proportional:
480 		if (wintype == wintype_TextBuffer) {
481 			p = val > 0;
482 			b = styles[style].isBold();
483 			i = styles[style].isItalic();
484 			styles[style].font = WindowStyle::makeFont(p, b, i);
485 		}
486 		break;
487 
488 	case stylehint_Weight:
489 		p = styles[style].isProp();
490 		b = val > 0;
491 		i = styles[style].isItalic();
492 		styles[style].font = WindowStyle::makeFont(p, b, i);
493 		break;
494 
495 	case stylehint_Oblique:
496 		p = styles[style].isProp();
497 		b = styles[style].isBold();
498 		i = val > 0;
499 		styles[style].font = WindowStyle::makeFont(p, b, i);
500 		break;
501 	}
502 
503 	if (wintype == wintype_TextBuffer && style == style_Normal && hint == stylehint_BackColor) {
504 		g_conf->_windowColor = styles[style].bg;
505 	}
506 
507 	if (wintype == wintype_TextBuffer && style == style_Normal && hint == stylehint_TextColor) {
508 		g_conf->_propInfo._moreColor = styles[style].fg;
509 		g_conf->_propInfo._caretColor = styles[style].fg;
510 	}
511 }
512 
glk_stylehint_clear(uint wintype,uint style,uint hint)513 void GlkAPI::glk_stylehint_clear(uint wintype, uint style, uint hint) {
514 	WindowStyle *styles;
515 	const WindowStyle *defaults;
516 
517 	if (wintype == wintype_AllTypes) {
518 		glk_stylehint_clear(wintype_TextGrid, style, hint);
519 		glk_stylehint_clear(wintype_TextBuffer, style, hint);
520 		return;
521 	}
522 
523 	if (wintype == wintype_TextGrid) {
524 		styles = g_conf->_gStyles;
525 		defaults = g_conf->_gStylesDefault;
526 	} else if (wintype == wintype_TextBuffer) {
527 		styles = g_conf->_tStyles;
528 		defaults = g_conf->_tStylesDefault;
529 	} else {
530 		return;
531 	}
532 
533 	if (!g_conf->_styleHint)
534 		return;
535 
536 	switch (hint) {
537 	case stylehint_TextColor:
538 		styles[style].fg = defaults[style].fg;
539 		break;
540 
541 	case stylehint_BackColor:
542 		styles[style].bg = defaults[style].bg;
543 		break;
544 
545 	case stylehint_ReverseColor:
546 		styles[style].reverse = defaults[style].reverse;
547 		break;
548 
549 	case stylehint_Proportional:
550 	case stylehint_Weight:
551 	case stylehint_Oblique:
552 		styles[style].font = defaults[style].font;
553 		break;
554 	}
555 }
556 
glk_style_distinguish(winid_t win,uint style1,uint style2)557 uint GlkAPI::glk_style_distinguish(winid_t win, uint style1, uint style2) {
558 	const WindowStyle *styles = win->getStyles();
559 	if (!styles)
560 		return false;
561 
562 	return styles[style1] == styles[style2] ? 0 : 1;
563 }
564 
glk_style_measure(winid_t win,uint style,uint hint,uint * result)565 bool GlkAPI::glk_style_measure(winid_t win, uint style, uint hint, uint *result) {
566 	const WindowStyle *styles = win->getStyles();
567 	if (!styles)
568 		return false;
569 
570 	switch (hint) {
571 	case stylehint_Indentation:
572 	case stylehint_ParaIndentation:
573 		*result = 0;
574 		break;
575 
576 	case stylehint_Justification:
577 		*result = stylehint_just_LeftFlush;
578 		break;
579 
580 	case stylehint_Size:
581 		*result = 1;
582 		break;
583 
584 	case stylehint_Weight:
585 		*result =
586 		    (styles[style].font == PROPB || styles[style].font == PROPZ ||
587 		     styles[style].font == MONOB || styles[style].font == MONOZ);
588 		break;
589 
590 	case stylehint_Oblique:
591 		*result =
592 		    (styles[style].font == PROPI || styles[style].font == PROPZ ||
593 		     styles[style].font == MONOI || styles[style].font == MONOZ);
594 		break;
595 
596 	case stylehint_Proportional:
597 		*result =
598 		    (styles[style].font == PROPR || styles[style].font == PROPI ||
599 		     styles[style].font == PROPB || styles[style].font == PROPZ);
600 		break;
601 
602 	case stylehint_TextColor:
603 		*result = styles[style].fg;
604 		break;
605 
606 	case stylehint_BackColor:
607 		*result = styles[style].bg;
608 		break;
609 
610 	case stylehint_ReverseColor:
611 		*result = styles[style].reverse;
612 		break;
613 
614 	default:
615 		return false;
616 	}
617 
618 	return true;
619 }
620 
glk_fileref_create_temp(uint usage,uint rock)621 frefid_t GlkAPI::glk_fileref_create_temp(uint usage, uint rock) {
622 	return _streams->createTemp(usage, rock);
623 }
624 
glk_fileref_create_by_name(uint usage,const char * name,uint rock)625 frefid_t GlkAPI::glk_fileref_create_by_name(uint usage, const char *name, uint rock) {
626 	// Take out all dangerous characters
627 	Common::String tempName(name);
628 	for (uint idx = 0; idx < tempName.size(); ++idx) {
629 		if (tempName[idx] == '/' || tempName[idx] == '\\' || tempName[idx] == ':')
630 			tempName.setChar(idx, '-');
631 	}
632 
633 	return _streams->createRef(tempName, usage, rock);
634 }
635 
glk_fileref_create_by_prompt(uint usage,FileMode fmode,uint rock)636 frefid_t GlkAPI::glk_fileref_create_by_prompt(uint usage, FileMode fmode, uint rock) {
637 	return _streams->createByPrompt(usage, fmode, rock);
638 }
639 
glk_fileref_create_from_fileref(uint usage,frefid_t fref,uint rock)640 frefid_t GlkAPI::glk_fileref_create_from_fileref(uint usage, frefid_t fref, uint rock) {
641 	if (!fref) {
642 		warning("fileref_create_from_fileref: invalid ref");
643 		return nullptr;
644 	} else {
645 		return _streams->createFromRef(fref, usage, rock);
646 	}
647 }
648 
glk_fileref_destroy(frefid_t fref)649 void GlkAPI::glk_fileref_destroy(frefid_t fref) {
650 	_streams->deleteRef(fref);
651 }
652 
glk_fileref_iterate(frefid_t fref,uint * rockptr)653 frefid_t GlkAPI::glk_fileref_iterate(frefid_t fref, uint *rockptr) {
654 	return _streams->iterate(fref, rockptr);
655 }
656 
glk_fileref_get_rock(frefid_t fref)657 uint GlkAPI::glk_fileref_get_rock(frefid_t fref) {
658 	if (!fref) {
659 		warning("fileref_get_rock: invalid ref.");
660 		return 0;
661 	} else {
662 		return fref->_rock;
663 	}
664 }
665 
glk_fileref_delete_file(frefid_t fref)666 void GlkAPI::glk_fileref_delete_file(frefid_t fref) {
667 	fref->deleteFile();
668 }
669 
glk_fileref_does_file_exist(frefid_t fref)670 uint GlkAPI::glk_fileref_does_file_exist(frefid_t fref) {
671 	return fref->exists();
672 }
673 
glk_select(event_t * event)674 void GlkAPI::glk_select(event_t *event) {
675 	if (!_gliFirstEvent) {
676 		_windows->inputGuessFocus();
677 		_gliFirstEvent = true;
678 	}
679 
680 	_events->getEvent(event, false);
681 }
682 
glk_select_poll(event_t * event)683 void GlkAPI::glk_select_poll(event_t *event) {
684 	if (!_gliFirstEvent) {
685 		_windows->inputGuessFocus();
686 		_gliFirstEvent = true;
687 	}
688 
689 	_events->getEvent(event, true);
690 }
691 
glk_request_timer_events(uint millisecs)692 void GlkAPI::glk_request_timer_events(uint millisecs) {
693 	_events->setTimerInterval(millisecs);
694 }
695 
glk_request_line_event(winid_t win,char * buf,uint maxlen,uint initlen)696 void GlkAPI::glk_request_line_event(winid_t win, char *buf, uint maxlen, uint initlen) {
697 	if (!win) {
698 		warning("request_line_event: invalid ref");
699 	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
700 	           || win->_lineRequestUni) {
701 		warning("request_line_event: window already has keyboard request");
702 	} else {
703 		win->requestLineEvent(buf, maxlen, initlen);
704 	}
705 }
706 
glk_request_char_event(winid_t win)707 void GlkAPI::glk_request_char_event(winid_t win) {
708 	if (!win) {
709 		warning("request_char_event: invalid ref");
710 	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
711 	           || win->_lineRequestUni) {
712 		warning("request_char_event: window already has keyboard request");
713 	} else {
714 		win->requestCharEvent();
715 	}
716 }
717 
glk_request_mouse_event(winid_t win)718 void GlkAPI::glk_request_mouse_event(winid_t win) {
719 	if (!win) {
720 		warning("request_mouse_event: invalid ref");
721 	} else {
722 		win->requestMouseEvent();
723 	}
724 }
725 
glk_cancel_line_event(winid_t win,event_t * event)726 void GlkAPI::glk_cancel_line_event(winid_t win, event_t *event) {
727 	if (!win) {
728 		warning("cancel_line_event: invalid ref");
729 	} else {
730 		win->cancelLineEvent(event);
731 	}
732 }
733 
glk_cancel_char_event(winid_t win)734 void GlkAPI::glk_cancel_char_event(winid_t win) {
735 	if (!win) {
736 		warning("glk_cancel_char_event: invalid ref");
737 	} else {
738 		win->cancelCharEvent();
739 	}
740 }
741 
glk_cancel_mouse_event(winid_t win)742 void GlkAPI::glk_cancel_mouse_event(winid_t win) {
743 	if (!win) {
744 		warning("cancel_mouse_event: invalid ref");
745 	} else {
746 		win->cancelMouseEvent();
747 	}
748 }
749 
glk_set_echo_line_event(winid_t win,uint val)750 void GlkAPI::glk_set_echo_line_event(winid_t win, uint val) {
751 	if (!win) {
752 		warning("set_echo_line_event: invalid ref");
753 	} else {
754 		win->setEchoLineEvent(val);
755 	}
756 }
757 
glk_set_terminators_line_event(winid_t win,const uint32 * keycodes,uint count)758 void GlkAPI::glk_set_terminators_line_event(winid_t win, const uint32 *keycodes, uint count) {
759 	if (!win) {
760 		warning("set_terminators_line_event: invalid ref");
761 	} else {
762 		win->setTerminatorsLineEvent(keycodes, count);
763 	}
764 }
765 
glk_buffer_to_lower_case_uni(uint32 * buf,uint len,uint numchars)766 uint GlkAPI::glk_buffer_to_lower_case_uni(uint32 *buf, uint len, uint numchars) {
767 	return bufferChangeCase(buf, len, numchars, CASE_LOWER, COND_ALL, true);
768 }
769 
glk_buffer_to_upper_case_uni(uint32 * buf,uint len,uint numchars)770 uint GlkAPI::glk_buffer_to_upper_case_uni(uint32 *buf, uint len, uint numchars) {
771 	return bufferChangeCase(buf, len, numchars, CASE_UPPER, COND_ALL, true);
772 }
773 
glk_buffer_to_title_case_uni(uint32 * buf,uint len,uint numchars,uint lowerrest)774 uint GlkAPI::glk_buffer_to_title_case_uni(uint32 *buf, uint len,
775         uint numchars, uint lowerrest) {
776 	return bufferChangeCase(buf, len, numchars, CASE_TITLE, COND_LINESTART, lowerrest);
777 }
778 
glk_put_char_uni(uint32 ch)779 void GlkAPI::glk_put_char_uni(uint32 ch) {
780 	_streams->getCurrent()->putCharUni(ch);
781 }
782 
glk_put_string_uni(const uint32 * s)783 void GlkAPI::glk_put_string_uni(const uint32 *s) {
784 	_streams->getCurrent()->putBufferUni(s, strlen_uni(s));
785 }
786 
glk_put_buffer_uni(const uint32 * buf,uint len)787 void GlkAPI::glk_put_buffer_uni(const uint32 *buf, uint len) {
788 	_streams->getCurrent()->putBufferUni(buf, len);
789 }
790 
glk_put_char_stream_uni(strid_t str,uint32 ch)791 void GlkAPI::glk_put_char_stream_uni(strid_t str, uint32 ch) {
792 	if (str) {
793 		str->putCharUni(ch);
794 	} else {
795 		warning("put_char_stream_uni: invalid ref");
796 	}
797 }
798 
glk_put_string_stream_uni(strid_t str,const uint32 * s)799 void GlkAPI::glk_put_string_stream_uni(strid_t str, const uint32 *s) {
800 	if (str) {
801 		str->putBufferUni(s, strlen_uni(s));
802 	} else {
803 		warning("put_string_stream_uni: invalid ref");
804 	}
805 }
806 
glk_put_buffer_stream_uni(strid_t str,const uint32 * buf,uint len)807 void GlkAPI::glk_put_buffer_stream_uni(strid_t str, const uint32 *buf, uint len) {
808 	if (str) {
809 		str->putBufferUni(buf, len);
810 	} else {
811 		warning("put_buffer_stream_uni: invalid ref");
812 	}
813 }
814 
glk_get_char_stream_uni(strid_t str)815 int GlkAPI::glk_get_char_stream_uni(strid_t str) {
816 	if (str) {
817 		return str->getCharUni();
818 	} else {
819 		warning("get_char_stream_uni: invalid ref");
820 		return -1;
821 	}
822 }
823 
glk_get_buffer_stream_uni(strid_t str,uint32 * buf,uint len)824 uint GlkAPI::glk_get_buffer_stream_uni(strid_t str, uint32 *buf, uint len) {
825 	if (str) {
826 		return str->getBufferUni(buf, len);
827 	} else {
828 		warning("get_buffer_stream_uni: invalid ref");
829 		return 0;
830 	}
831 }
832 
glk_get_line_stream_uni(strid_t str,uint32 * buf,uint len)833 uint GlkAPI::glk_get_line_stream_uni(strid_t str, uint32 *buf, uint len) {
834 	if (str) {
835 		return str->getLineUni(buf, len);
836 	} else  {
837 		warning("get_line_stream_uni: invalid ref");
838 		return (uint) - 1;
839 	}
840 }
841 
glk_stream_open_file_uni(frefid_t fileref,FileMode fmode,uint rock)842 strid_t GlkAPI::glk_stream_open_file_uni(frefid_t fileref, FileMode fmode, uint rock) {
843 	return _streams->openFileStream(fileref, fmode, rock, true);
844 }
845 
glk_stream_open_memory_uni(uint32 * buf,uint buflen,FileMode fmode,uint rock)846 strid_t GlkAPI::glk_stream_open_memory_uni(uint32 *buf, uint buflen, FileMode fmode, uint rock) {
847 	return _streams->openMemoryStream(buf, buflen, fmode, rock, true);
848 }
849 
glk_request_char_event_uni(winid_t win)850 void GlkAPI::glk_request_char_event_uni(winid_t win) {
851 	if (!win) {
852 		warning("request_char_event_uni: invalid ref");
853 	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
854 	           || win->_lineRequestUni) {
855 		warning("request_char_event_uni: window already has keyboard request");
856 	} else {
857 		win->requestCharEvent();
858 	}
859 }
860 
glk_request_line_event_uni(winid_t win,uint32 * buf,uint maxlen,uint initlen)861 void GlkAPI::glk_request_line_event_uni(winid_t win, uint32 *buf, uint maxlen, uint initlen) {
862 	if (!win) {
863 		warning("request_line_event_uni: invalid ref");
864 	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
865 	           || win->_lineRequestUni) {
866 		warning("request_line_event_uni: window already has keyboard request");
867 	} else {
868 		win->requestLineEventUni(buf, maxlen, initlen);
869 	}
870 }
871 
glk_buffer_canon_decompose_uni(uint32 * buf,uint len,uint numchars)872 uint GlkAPI::glk_buffer_canon_decompose_uni(uint32 *buf, uint len, uint numchars) {
873 	// TODO
874 	return 0;
875 }
876 
glk_buffer_canon_normalize_uni(uint32 * buf,uint len,uint numchars)877 uint GlkAPI::glk_buffer_canon_normalize_uni(uint32 *buf, uint len, uint numchars) {
878 	return 0;
879 }
880 
glk_image_draw(winid_t win,uint image,int val1,int val2)881 bool GlkAPI::glk_image_draw(winid_t win, uint image, int val1, int val2) {
882 	if (!win) {
883 		warning("image_draw: invalid ref");
884 	} else if (g_conf->_graphics) {
885 		TextBufferWindow *textWin = dynamic_cast<TextBufferWindow *>(win);
886 		GraphicsWindow *gfxWin = dynamic_cast<GraphicsWindow *>(win);
887 
888 		if (textWin)
889 			return textWin->drawPicture(image, val1, false, 0, 0);
890 		else if (gfxWin)
891 			return gfxWin->drawPicture(image, val1, val2, false, 0, 0);
892 	}
893 
894 	return false;
895 }
896 
glk_image_draw_scaled(winid_t win,uint image,int val1,int val2,uint width,uint height)897 bool GlkAPI::glk_image_draw_scaled(winid_t win, uint image, int val1, int val2,
898                                   uint width, uint height) {
899 	if (!win) {
900 		warning("image_draw_scaled: invalid ref");
901 	} else if (g_conf->_graphics) {
902 		TextBufferWindow *textWin = dynamic_cast<TextBufferWindow *>(win);
903 		GraphicsWindow *gfxWin = dynamic_cast<GraphicsWindow *>(win);
904 
905 		if (textWin)
906 			return textWin->drawPicture(image, val1, true, width, height);
907 		else if (gfxWin)
908 			return gfxWin->drawPicture(image, val1, val2, true, width, height);
909 	}
910 
911 	return false;
912 }
913 
glk_image_get_info(uint image,uint * width,uint * height)914 bool GlkAPI::glk_image_get_info(uint image, uint *width, uint *height) {
915 	if (!g_conf->_graphics)
916 		return false;
917 
918 	Picture *pic = g_vm->_pictures->load(image);
919 	if (!pic)
920 		return false;
921 
922 	if (width)
923 		*width = pic->w;
924 	if (height)
925 		*height = pic->h;
926 
927 	return true;
928 }
929 
glk_window_flow_break(winid_t win)930 void GlkAPI::glk_window_flow_break(winid_t win) {
931 	if (!win) {
932 		warning("window_erase_rect: invalid ref");
933 	} else {
934 		win->flowBreak();
935 	}
936 }
937 
glk_window_erase_rect(winid_t win,int left,int top,uint width,uint height)938 void GlkAPI::glk_window_erase_rect(winid_t win, int left, int top, uint width, uint height) {
939 	if (!win) {
940 		warning("window_erase_rect: invalid ref");
941 	} else {
942 		win->eraseRect(false, Rect(left, top, left + width, top + height));
943 	}
944 }
945 
glk_window_fill_rect(winid_t win,uint color,int left,int top,uint width,uint height)946 void GlkAPI::glk_window_fill_rect(winid_t win, uint color, int left, int top,
947                                uint width, uint height) {
948 	if (!win) {
949 		warning("window_fill_rect: invalid ref");
950 	} else {
951 		win->eraseRect(color, Rect(left, top, left + width, top + height));
952 	}
953 }
954 
glk_window_set_background_color(winid_t win,uint color)955 void GlkAPI::glk_window_set_background_color(winid_t win, uint color) {
956 	if (!win) {
957 		warning("window_set_background_color: invalid ref");
958 	} else {
959 		win->setBackgroundColor(color);
960 	}
961 }
962 
glk_schannel_create(uint rock)963 schanid_t GlkAPI::glk_schannel_create(uint rock) {
964 	return _sounds->create(rock);
965 }
966 
glk_schannel_destroy(schanid_t chan)967 void GlkAPI::glk_schannel_destroy(schanid_t chan) {
968 	if (chan) {
969 		delete chan;
970 	} else {
971 		warning("schannel_dest roy: invalid ref");
972 	}
973 }
974 
glk_schannel_iterate(schanid_t chan,uint * rockptr)975 schanid_t GlkAPI::glk_schannel_iterate(schanid_t chan, uint *rockptr) {
976 	return _sounds->iterate(chan, rockptr);
977 }
978 
glk_schannel_get_rock(schanid_t chan)979 uint GlkAPI::glk_schannel_get_rock(schanid_t chan) {
980 	if (chan) {
981 		return chan->_rock;
982 	} else {
983 		warning("schannel_get_rock: invalid ref");
984 		return 0;
985 	}
986 }
987 
glk_schannel_play(schanid_t chan,uint snd)988 uint GlkAPI::glk_schannel_play(schanid_t chan, uint snd) {
989 	if (chan) {
990 		return chan->play(snd);
991 	} else {
992 		warning("schannel_play_ext: invalid ref");
993 		return 0;
994 	}
995 }
996 
glk_schannel_play_ext(schanid_t chan,uint snd,uint repeats,uint notify)997 uint GlkAPI::glk_schannel_play_ext(schanid_t chan, uint snd, uint repeats, uint notify) {
998 	if (chan) {
999 		return chan->play(snd, repeats, notify);
1000 	} else {
1001 		warning("schannel_play_ext: invalid ref");
1002 		return 0;
1003 	}
1004 }
1005 
glk_schannel_stop(schanid_t chan)1006 void GlkAPI::glk_schannel_stop(schanid_t chan) {
1007 	if (chan) {
1008 		chan->stop();
1009 	} else {
1010 		warning("schannel_stop: invalid ref");
1011 	}
1012 }
1013 
glk_schannel_set_volume(schanid_t chan,uint vol)1014 void GlkAPI::glk_schannel_set_volume(schanid_t chan, uint vol) {
1015 	if (chan) {
1016 		chan->setVolume(vol);
1017 	} else {
1018 		warning("schannel_set_volume: invalid ref");
1019 	}
1020 }
1021 
glk_sound_load_hint(uint snd,uint flag)1022 void GlkAPI::glk_sound_load_hint(uint snd, uint flag) {
1023 	// No implementation
1024 }
1025 
glk_schannel_create_ext(uint rock,uint volume)1026 schanid_t GlkAPI::glk_schannel_create_ext(uint rock, uint volume) {
1027 	// No implementation
1028 	return nullptr;
1029 }
1030 
glk_schannel_play_multi(schanid_t * chanarray,uint chancount,uint * sndarray,uint soundcount,uint notify)1031 uint GlkAPI::glk_schannel_play_multi(schanid_t *chanarray, uint chancount,
1032                                     uint *sndarray, uint soundcount, uint notify) {
1033 	// No implementation
1034 	return 0;
1035 }
1036 
glk_schannel_pause(schanid_t chan)1037 void GlkAPI::glk_schannel_pause(schanid_t chan) {
1038 	if (chan) {
1039 		chan->pause();
1040 	} else {
1041 		warning("schannel_pause: invalid ref");
1042 	}
1043 }
1044 
glk_schannel_unpause(schanid_t chan)1045 void GlkAPI::glk_schannel_unpause(schanid_t chan) {
1046 	if (chan) {
1047 		chan->unpause();
1048 	} else {
1049 		warning("schannel_unpause: invalid ref");
1050 	}
1051 }
1052 
glk_schannel_set_volume_ext(schanid_t chan,uint vol,uint duration,uint notify)1053 void GlkAPI::glk_schannel_set_volume_ext(schanid_t chan, uint vol,
1054                                       uint duration, uint notify) {
1055 	if (chan) {
1056 		chan->setVolume(vol, duration, notify);
1057 	} else {
1058 		warning("schannel_set_volume_ext: invalid ref");
1059 	}
1060 }
1061 
glk_set_hyperlink(uint linkval)1062 void GlkAPI::glk_set_hyperlink(uint linkval) {
1063 	_streams->getCurrent()->setHyperlink(linkval);
1064 }
1065 
glk_set_hyperlink_stream(strid_t str,uint linkval)1066 void GlkAPI::glk_set_hyperlink_stream(strid_t str, uint linkval) {
1067 	if (str)
1068 		str->setHyperlink(linkval);
1069 }
1070 
glk_request_hyperlink_event(winid_t win)1071 void GlkAPI::glk_request_hyperlink_event(winid_t win) {
1072 	if (!win) {
1073 		warning("request_hyperlink_event: invalid ref");
1074 	} else {
1075 		win->requestHyperlinkEvent();
1076 	}
1077 }
1078 
glk_cancel_hyperlink_event(winid_t win)1079 void GlkAPI::glk_cancel_hyperlink_event(winid_t win) {
1080 	if (win) {
1081 		win->cancelHyperlinkEvent();
1082 	} else {
1083 		warning("cancel_hyperlink_event: invalid ref");
1084 	}
1085 }
1086 
1087 /*--------------------------------------------------------------------------*/
1088 
glk_current_time(glktimeval_t * time)1089 void GlkAPI::glk_current_time(glktimeval_t *time) {
1090 	TimeAndDate td;
1091 	*time = td;
1092 }
1093 
glk_current_simple_time(uint factor)1094 int GlkAPI::glk_current_simple_time(uint factor) {
1095 	assert(factor);
1096 	TimeAndDate td;
1097 
1098 	return td / factor;
1099 }
1100 
glk_time_to_date_utc(const glktimeval_t * time,glkdate_t * date)1101 void GlkAPI::glk_time_to_date_utc(const glktimeval_t *time, glkdate_t *date) {
1102 	// TODO: timezones aren't currently supported
1103 	*date = TimeAndDate(*time);
1104 }
1105 
glk_time_to_date_local(const glktimeval_t * time,glkdate_t * date)1106 void GlkAPI::glk_time_to_date_local(const glktimeval_t *time, glkdate_t *date) {
1107 	*date = TimeAndDate(*time);
1108 }
1109 
glk_simple_time_to_date_utc(int time,uint factor,glkdate_t * date)1110 void GlkAPI::glk_simple_time_to_date_utc(int time, uint factor, glkdate_t *date) {
1111 	TimeSeconds secs = (int64)time * factor;
1112 	*date = TimeAndDate(secs);
1113 }
1114 
glk_simple_time_to_date_local(int time,uint factor,glkdate_t * date)1115 void GlkAPI::glk_simple_time_to_date_local(int time, uint factor, glkdate_t *date) {
1116 	TimeSeconds secs = (int64)time * factor;
1117 	*date = TimeAndDate(secs);
1118 }
1119 
glk_date_to_time_utc(const glkdate_t * date,glktimeval_t * time)1120 void GlkAPI::glk_date_to_time_utc(const glkdate_t *date, glktimeval_t *time) {
1121 	// WORKAROUND: timezones aren't currently supported
1122 	*time = TimeAndDate(*date);
1123 }
1124 
glk_date_to_time_local(const glkdate_t * date,glktimeval_t * time)1125 void GlkAPI::glk_date_to_time_local(const glkdate_t *date, glktimeval_t *time) {
1126 	*time = TimeAndDate(*date);
1127 }
1128 
glk_date_to_simple_time_utc(const glkdate_t * date,uint factor)1129 int GlkAPI::glk_date_to_simple_time_utc(const glkdate_t *date, uint factor) {
1130 	// WORKAROUND: timezones aren't currently supported
1131 	assert(factor);
1132 	TimeSeconds ts = TimeAndDate(*date);
1133 	return ts / factor;
1134 }
1135 
glk_date_to_simple_time_local(const glkdate_t * date,uint factor)1136 int GlkAPI::glk_date_to_simple_time_local(const glkdate_t *date, uint factor) {
1137 	assert(factor);
1138 	TimeSeconds ts = TimeAndDate(*date);
1139 	return ts / factor;
1140 }
1141 
1142 /*--------------------------------------------------------------------------*/
1143 
1144 /* XXX non-official Glk functions */
1145 
garglk_fileref_get_name(frefid_t fref) const1146 const char *GlkAPI::garglk_fileref_get_name(frefid_t fref) const {
1147 	return fref->_filename.c_str();
1148 }
1149 
garglk_set_program_name(const char * name)1150 void GlkAPI::garglk_set_program_name(const char *name) {
1151 	// Program name isn't displayed
1152 }
1153 
garglk_set_program_info(const char * info)1154 void GlkAPI::garglk_set_program_info(const char *info) {
1155 	// Program info isn't displayed
1156 }
1157 
garglk_set_story_name(const char * name)1158 void GlkAPI::garglk_set_story_name(const char *name) {
1159 	// Story name isn't displayed
1160 }
1161 
garglk_set_story_title(const char * title)1162 void GlkAPI::garglk_set_story_title(const char *title) {
1163 	// Story title isn't displayed
1164 }
1165 
garglk_set_config(const char * name)1166 void GlkAPI::garglk_set_config(const char *name) {
1167 	// No implementation
1168 }
1169 
garglk_unput_string(const char * str)1170 void GlkAPI::garglk_unput_string(const char *str) {
1171 	_streams->getCurrent()->unputBuffer(str, strlen(str));
1172 }
1173 
garglk_unput_string_uni(const uint32 * str)1174 void GlkAPI::garglk_unput_string_uni(const uint32 *str) {
1175 	_streams->getCurrent()->unputBufferUni(str, strlen_uni(str));
1176 }
1177 
garglk_set_zcolors(uint fg,uint bg)1178 void GlkAPI::garglk_set_zcolors(uint fg, uint bg) {
1179 	_streams->getCurrent()->setZColors(fg, bg);
1180 }
1181 
garglk_set_zcolors_stream(strid_t str,uint fg,uint bg)1182 void GlkAPI::garglk_set_zcolors_stream(strid_t str, uint fg, uint bg) {
1183 	if (str) {
1184 		str->setZColors(fg, bg);
1185 	} else {
1186 		warning("set_style_stream: Invalid ref");
1187 	}
1188 }
1189 
garglk_set_reversevideo(uint reverse)1190 void GlkAPI::garglk_set_reversevideo(uint reverse) {
1191 	_streams->getCurrent()->setReverseVideo(reverse != 0);
1192 }
1193 
garglk_set_reversevideo_stream(strid_t str,uint reverse)1194 void GlkAPI::garglk_set_reversevideo_stream(strid_t str, uint reverse) {
1195 	if (str) {
1196 		str->setReverseVideo(reverse != 0);
1197 	} else {
1198 		warning("set_reversevideo: Invalid ref");
1199 	}
1200 }
1201 
garglk_window_get_cursor(winid_t win,uint * xpos,uint * ypos)1202 void GlkAPI::garglk_window_get_cursor(winid_t win, uint *xpos, uint *ypos) {
1203 	Point pos = win->getCursor();
1204 	*xpos = pos.x;
1205 	*ypos = pos.y;
1206 }
1207 
garglk_window_get_cursor_current(uint * xpos,uint * ypos)1208 void GlkAPI::garglk_window_get_cursor_current(uint *xpos, uint *ypos) {
1209 	garglk_window_get_cursor(_windows->getFocusWindow(), xpos, ypos);
1210 }
1211 
1212 } // End of namespace Glk
1213