check if we have a working C++ compiler
[rofl0r-df-libgraphics.git] / g_src / enabler.cpp
blob7a33c544e029a2870176b3c8ce0255218b429935
1 #ifdef __APPLE__
2 # include "osx_messagebox.h"
3 #elif defined(unix) && defined(HAVE_GTK2)
4 # include <gtk/gtk.h>
5 #endif
7 #include <unistd.h>
8 #include <cassert>
10 #include "platform.h"
11 #include "enabler.h"
12 #include "random.h"
13 #include "init.h"
14 #include "music_and_sound_g.h"
16 #ifdef unix
17 # include <locale.h>
18 #endif
20 using namespace std;
22 enablerst enabler;
25 // For the printGLError macro
26 int glerrorcount = 0;
28 // Set to 0 when the game wants to quit
29 static int loopvar = 1;
31 // Reports an error to the user, using a MessageBox and stderr.
32 void report_error(const char *error_preface, const char *error_message)
34 char *buf = NULL;
35 // +4 = +colon +space +newline +nul
36 buf = new char[strlen(error_preface) + strlen(error_message) + 4];
37 sprintf(buf, "%s: %s\n", error_preface, error_message);
38 MessageBox(NULL, buf, "Error", MB_OK);
39 fprintf(stderr, "%s", buf);
40 delete [] buf;
43 Either<texture_fullid,texture_ttfid> renderer::screen_to_texid(int x, int y) {
44 const int tile = x * gps.dimy + y;
45 const unsigned char *s = screen + tile*4;
47 struct texture_fullid ret;
48 int ch;
49 int bold;
50 int fg;
51 int bg;
53 // TTF text does not get the full treatment.
54 if (s[3] == GRAPHICSTYPE_TTF) {
55 texture_ttfid texpos = *((unsigned int *)s) & 0xffffff;
56 return Either<texture_fullid,texture_ttfid>(texpos);
57 } else if (s[3] == GRAPHICSTYPE_TTFCONT) {
58 // TTFCONT means this is a tile that does not have TTF anchored on it, but is covered by TTF.
59 // Since this may actually be stale information, we'll draw it as a blank space,
60 ch = 32;
61 fg = bg = bold = 0;
62 } else {
63 // Otherwise, it's a normal (graphical?) tile.
64 ch = s[0];
65 bold = (s[3] != 0) * 8;
66 fg = (s[1] + bold) % 16;
67 bg = s[2] % 16;
70 static bool use_graphics = init.display.flag.has_flag(INIT_DISPLAY_FLAG_USE_GRAPHICS);
72 if (use_graphics) {
73 const long texpos = screentexpos[tile];
74 const char addcolor = screentexpos_addcolor[tile];
75 const unsigned char grayscale = screentexpos_grayscale[tile];
76 const unsigned char cf = screentexpos_cf[tile];
77 const unsigned char cbr = screentexpos_cbr[tile];
79 if (texpos) {
80 ret.texpos = texpos;
81 if (grayscale) {
82 ret.r = enabler.ccolor[cf][0];
83 ret.g = enabler.ccolor[cf][1];
84 ret.b = enabler.ccolor[cf][2];
85 ret.br = enabler.ccolor[cbr][0];
86 ret.bg = enabler.ccolor[cbr][1];
87 ret.bb = enabler.ccolor[cbr][2];
88 } else if (addcolor) {
89 goto use_ch;
90 } else {
91 ret.r = ret.g = ret.b = 1;
92 ret.br = ret.bg = ret.bb = 0;
94 goto skip_ch;
98 ret.texpos = enabler.is_fullscreen() ?
99 init.font.large_font_texpos[ch] :
100 init.font.small_font_texpos[ch];
101 use_ch:
102 ret.r = enabler.ccolor[fg][0];
103 ret.g = enabler.ccolor[fg][1];
104 ret.b = enabler.ccolor[fg][2];
105 ret.br = enabler.ccolor[bg][0];
106 ret.bg = enabler.ccolor[bg][1];
107 ret.bb = enabler.ccolor[bg][2];
109 skip_ch:
111 return Either<texture_fullid,texture_ttfid>(ret);
115 #ifdef CURSES
116 # include "renderer_curses.cpp"
117 #endif
118 #include "renderer_2d.hpp"
119 #include "renderer_opengl.hpp"
122 enablerst::enablerst() {
123 fullscreen = false;
124 #ifdef WANT_GL
125 sync = NULL;
126 #endif
127 renderer = NULL;
128 calculated_fps = calculated_gfps = frame_sum = gframe_sum = frame_last = gframe_last = 0;
129 fps = 100; gfps = 20;
130 fps_per_gfps = fps / gfps;
131 last_tick = 0;
134 void renderer::display()
136 const int dimx = init.display.grid_x;
137 const int dimy = init.display.grid_y;
138 static bool use_graphics = init.display.flag.has_flag(INIT_DISPLAY_FLAG_USE_GRAPHICS);
139 if (gps.force_full_display_count) {
140 // Update the entire screen
141 update_all();
142 } else {
143 Uint32 *screenp = (Uint32*)screen, *oldp = (Uint32*)screen_old;
144 if (use_graphics) {
145 int off = 0;
146 for (int x2=0; x2 < dimx; x2++) {
147 for (int y2=0; y2 < dimy; y2++, ++off, ++screenp, ++oldp) {
148 // We don't use pointers for the non-screen arrays because we mostly fail at the
149 // *first* comparison, and having pointers for the others would exceed register
150 // count.
151 // Partial printing (and color-conversion): Big-ass if.
152 if (*screenp == *oldp &&
153 screentexpos[off] == screentexpos_old[off] &&
154 screentexpos_addcolor[off] == screentexpos_addcolor_old[off] &&
155 screentexpos_grayscale[off] == screentexpos_grayscale_old[off] &&
156 screentexpos_cf[off] == screentexpos_cf_old[off] &&
157 screentexpos_cbr[off] == screentexpos_cbr_old[off])
159 // Nothing's changed, this clause deliberately empty
160 } else {
161 update_tile(x2, y2);
165 } else {
166 for (int x2=0; x2 < dimx; ++x2) {
167 for (int y2=0; y2 < dimy; ++y2, ++screenp, ++oldp) {
168 if (*screenp != *oldp) {
169 update_tile(x2, y2);
175 if (gps.force_full_display_count > 0) gps.force_full_display_count--;
178 void renderer::cleanup_arrays() {
179 if (screen) delete[] screen;
180 if (screentexpos) delete[] screentexpos;
181 if (screentexpos_addcolor) delete[] screentexpos_addcolor;
182 if (screentexpos_grayscale) delete[] screentexpos_grayscale;
183 if (screentexpos_cf) delete[] screentexpos_cf;
184 if (screentexpos_cbr) delete[] screentexpos_cbr;
185 if (screen_old) delete[] screen_old;
186 if (screentexpos_old) delete[] screentexpos_old;
187 if (screentexpos_addcolor_old) delete[] screentexpos_addcolor_old;
188 if (screentexpos_grayscale_old) delete[] screentexpos_grayscale_old;
189 if (screentexpos_cf_old) delete[] screentexpos_cf_old;
190 if (screentexpos_cbr_old) delete[] screentexpos_cbr_old;
193 void renderer::gps_allocate(int x, int y) {
194 cleanup_arrays();
196 gps.screen = screen = new unsigned char[x*y*4];
197 memset(screen, 0, x*y*4);
198 gps.screentexpos = screentexpos = new long[x*y];
199 memset(screentexpos, 0, x*y*sizeof(long));
200 gps.screentexpos_addcolor = screentexpos_addcolor = new char[x*y];
201 memset(screentexpos_addcolor, 0, x*y);
202 gps.screentexpos_grayscale = screentexpos_grayscale = new unsigned char[x*y];
203 memset(screentexpos_grayscale, 0, x*y);
204 gps.screentexpos_cf = screentexpos_cf = new unsigned char[x*y];
205 memset(screentexpos_cf, 0, x*y);
206 gps.screentexpos_cbr = screentexpos_cbr = new unsigned char[x*y];
207 memset(screentexpos_cbr, 0, x*y);
209 screen_old = new unsigned char[x*y*4];
210 memset(screen_old, 0, x*y*4);
211 screentexpos_old = new long[x*y];
212 memset(screentexpos_old, 0, x*y*sizeof(long));
213 screentexpos_addcolor_old = new char[x*y];
214 memset(screentexpos_addcolor_old, 0, x*y);
215 screentexpos_grayscale_old = new unsigned char[x*y];
216 memset(screentexpos_grayscale_old, 0, x*y);
217 screentexpos_cf_old = new unsigned char[x*y];
218 memset(screentexpos_cf_old, 0, x*y);
219 screentexpos_cbr_old = new unsigned char[x*y];
220 memset(screentexpos_cbr_old, 0, x*y);
222 gps.resize(x,y);
225 void renderer::swap_arrays() {
226 screen = screen_old; screen_old = gps.screen; gps.screen = screen;
227 screentexpos = screentexpos_old; screentexpos_old = gps.screentexpos; gps.screentexpos = screentexpos;
228 screentexpos_addcolor = screentexpos_addcolor_old; screentexpos_addcolor_old = gps.screentexpos_addcolor; gps.screentexpos_addcolor = screentexpos_addcolor;
229 screentexpos_grayscale = screentexpos_grayscale_old; screentexpos_grayscale_old = gps.screentexpos_grayscale; gps.screentexpos_grayscale = screentexpos_grayscale;
230 screentexpos_cf = screentexpos_cf_old; screentexpos_cf_old = gps.screentexpos_cf; gps.screentexpos_cf = screentexpos_cf;
231 screentexpos_cbr = screentexpos_cbr_old; screentexpos_cbr_old = gps.screentexpos_cbr; gps.screentexpos_cbr = screentexpos_cbr;
233 gps.screen_limit = gps.screen + gps.dimx * gps.dimy * 4;
236 void enablerst::pause_async_loop() {
237 struct async_cmd cmd;
238 cmd.cmd = async_cmd::pause;
239 async_tobox.write(cmd);
240 async_wait();
243 // Wait until the previous command has been acknowledged, /or/
244 // async_loop has quit. Incidentally execute any requests in the
245 // meantime.
246 void enablerst::async_wait() {
247 if (loopvar == 0) return;
248 async_msg r;
249 bool reset_textures = false;
250 for (;;) {
251 async_frombox.read(r);
252 switch (r.msg) {
253 case async_msg::quit:
254 loopvar = 0;
255 return;
256 case async_msg::complete:
257 if (reset_textures) {
258 puts("Resetting textures");
259 textures.remove_uploaded_textures();
260 textures.upload_textures();
262 return;
263 case async_msg::set_fps:
264 set_fps(r.fps);
265 async_fromcomplete.write();
266 break;
267 case async_msg::set_gfps:
268 set_gfps(r.fps);
269 async_fromcomplete.write();
270 break;
271 case async_msg::push_resize:
272 override_grid_size(r.x, r.y);
273 async_fromcomplete.write();
274 break;
275 case async_msg::pop_resize:
276 release_grid_size();
277 async_fromcomplete.write();
278 break;
279 case async_msg::reset_textures:
280 reset_textures = true;
281 break;
282 default:
283 puts("EMERGENCY: Unknown case in async_wait");
284 abort();
289 void enablerst::async_loop() {
290 async_paused = false;
291 async_frames = 0;
292 int total_frames = 0;
293 int fps = 100; // Just a thread-local copy
294 for (;;) {
295 // cout << "FRAMES: " << frames << endl;
296 // Check for commands
297 async_cmd cmd;
298 bool have_cmd = true;
299 do {
300 if (async_paused || (async_frames == 0 && !(enabler.flag & ENABLERFLAG_MAXFPS)))
301 async_tobox.read(cmd);
302 else
303 have_cmd = async_tobox.try_read(cmd);
304 // Obey the command, would you kindly.
305 if (have_cmd) {
306 switch (cmd.cmd) {
307 case async_cmd::pause:
308 async_paused = true;
309 // puts("Paused");
310 async_frombox.write(async_msg(async_msg::complete));
311 break;
312 case async_cmd::start:
313 async_paused = false;
314 async_frames = 0;
315 // puts("UNpaused");
316 break;
317 case async_cmd::render:
318 if (flag & ENABLERFLAG_RENDER) {
319 total_frames++;
320 renderer->swap_arrays();
321 if (total_frames % 1800 == 0)
322 ttf_manager.gc();
323 render_things();
324 flag &= ~ENABLERFLAG_RENDER;
325 update_gfps();
327 async_frombox.write(async_msg(async_msg::complete));
328 break;
329 case async_cmd::inc:
330 async_frames += cmd.val;
331 if (async_frames > fps*3) async_frames = fps*3; // Just in case
332 break;
333 case async_cmd::set_fps:
334 fps = cmd.val;
335 break;
338 } while (have_cmd);
339 // Run the main-loop, maybe
340 if (!async_paused && (async_frames || (enabler.flag & ENABLERFLAG_MAXFPS))) {
341 if (mainloop()) {
342 async_frombox.write(async_msg(async_msg::quit));
343 return; // We're done.
345 simticks.lock();
346 simticks.val++;
347 simticks.unlock();
348 async_frames--;
349 if (async_frames < 0) async_frames = 0;
350 update_fps();
352 SDL_NumJoysticks(); // Hook for dfhack
356 void enablerst::do_frame() {
357 // Check how long it's been, exactly
358 const Uint32 now = SDL_GetTicks();
359 const Uint32 interval = CLAMP(now - last_tick, 0, 1000); // Anything above a second doesn't count
360 // cout << last_tick << " + " << interval << " = " << now << endl;
361 last_tick = now;
363 // Update outstanding-frame counts
364 outstanding_frames += interval * fps / 1000;
365 outstanding_gframes += interval * gfps / 1000;
366 if (outstanding_gframes > 3) {
367 outstanding_gframes = 3;
369 // cout << outstanding_frames << " " << outstanding_gframes << endl;
371 // Update the loop's tick-counter suitably
372 if (outstanding_frames >= 1) {
373 async_cmd cmd(async_cmd::inc);
374 cmd.val = outstanding_frames;
375 outstanding_frames -= cmd.val;
376 async_tobox.write(cmd);
379 // Store the current time, for things that are fine with approximations
380 enabler.clock = SDL_GetTicks();
382 // If it's time to render..
383 if (outstanding_gframes >= 1
384 #ifdef WANT_GL
386 (!sync || glClientWaitSync(sync, 0, 0) == GL_ALREADY_SIGNALED)
387 #endif
390 // Get the async-loop to render_things
391 async_cmd cmd(async_cmd::render);
392 async_tobox.write(cmd);
393 async_wait();
394 // Then finish here
395 renderer->display();
396 renderer->render();
397 gputicks.lock();
398 gputicks.val++;
399 gputicks.unlock();
400 outstanding_gframes--;
403 // Sleep until the next gframe
404 if (outstanding_gframes < 1) {
405 float fragment = 1 - outstanding_gframes;
406 float milliseconds = fragment / gfps * 1000;
407 // cout << milliseconds << endl;
408 SDL_Delay(milliseconds);
412 void enablerst::eventLoop_SDL()
415 SDL_Event event;
416 const SDL_Surface *screen = SDL_GetVideoSurface();
417 Uint32 mouse_lastused = 0;
418 SDL_ShowCursor(SDL_DISABLE);
420 // Initialize the grid
421 renderer->resize(screen->w, screen->h);
423 while (loopvar) {
424 Uint32 now = SDL_GetTicks();
425 bool paused_loop = false;
427 // Check for zoom commands
428 zoom_commands zoom;
429 while (async_zoom.try_read(zoom)) {
430 if (overridden_grid_sizes.size())
431 continue; // No zooming in movies
432 if (!paused_loop) {
433 pause_async_loop();
434 paused_loop = true;
436 if (zoom == zoom_fullscreen)
437 renderer->set_fullscreen();
438 else
439 renderer->zoom(zoom);
442 // Check for SDL events
443 while (SDL_PollEvent(&event)) {
444 // Make sure mainloop isn't running while we're processing input
445 if (!paused_loop) {
446 pause_async_loop();
447 paused_loop = true;
449 // Handle SDL events
450 switch (event.type) {
451 case SDL_KEYDOWN:
452 // Disable mouse if it's been long enough
453 if (mouse_lastused + 5000 < now) {
454 if(init.input.flag.has_flag(INIT_INPUT_FLAG_MOUSE_PICTURE)) {
455 // hide the mouse picture
456 // enabler.set_tile(0, TEXTURE_MOUSE, enabler.mouse_x, enabler.mouse_y);
458 SDL_ShowCursor(SDL_DISABLE);
460 case SDL_KEYUP:
461 case SDL_QUIT:
462 enabler.add_input(event, now);
463 break;
464 case SDL_MOUSEBUTTONDOWN:
465 case SDL_MOUSEBUTTONUP:
466 if (!init.input.flag.has_flag(INIT_INPUT_FLAG_MOUSE_OFF)) {
467 int isdown = (event.type == SDL_MOUSEBUTTONDOWN);
468 if (event.button.button == SDL_BUTTON_LEFT) {
469 enabler.mouse_lbut = isdown;
470 enabler.mouse_lbut_down = isdown;
471 if (!isdown)
472 enabler.mouse_lbut_lift = 0;
473 } else if (event.button.button == SDL_BUTTON_RIGHT) {
474 enabler.mouse_rbut = isdown;
475 enabler.mouse_rbut_down = isdown;
476 if (!isdown)
477 enabler.mouse_rbut_lift = 0;
478 } else
479 enabler.add_input(event, now);
481 break;
482 case SDL_MOUSEMOTION:
483 // Deal with the mouse hiding bit
484 mouse_lastused = now;
485 if(init.input.flag.has_flag(INIT_INPUT_FLAG_MOUSE_PICTURE)) {
486 // turn on mouse picture
487 // enabler.set_tile(gps.tex_pos[TEXTURE_MOUSE], TEXTURE_MOUSE,enabler.mouse_x, enabler.mouse_y);
488 } else {
489 SDL_ShowCursor(SDL_ENABLE);
491 break;
492 case SDL_ACTIVEEVENT:
493 enabler.clear_input();
494 if (event.active.state & SDL_APPACTIVE) {
495 if (event.active.gain) {
496 enabler.flag|=ENABLERFLAG_RENDER;
497 gps.force_full_display_count++;
500 break;
501 case SDL_VIDEOEXPOSE:
502 gps.force_full_display_count++;
503 enabler.flag|=ENABLERFLAG_RENDER;
504 break;
505 case SDL_VIDEORESIZE:
506 if (is_fullscreen());
507 //errorlog << "Caught resize event in fullscreen??\n";
508 else {
509 //gamelog << "Resizing window to " << event.resize.w << "x" << event.resize.h << endl << flush;
510 renderer->resize(event.resize.w, event.resize.h);
512 break;
513 } // switch (event.type)
514 } //while have event
516 // Update mouse state
517 if (!init.input.flag.has_flag(INIT_INPUT_FLAG_MOUSE_OFF)) {
518 int mouse_x = -1, mouse_y = -1, mouse_state;
519 // Check whether the renderer considers this valid input or not, and write it to gps
520 if ((SDL_GetAppState() & SDL_APPMOUSEFOCUS) &&
521 renderer->get_mouse_coords(mouse_x, mouse_y)) {
522 mouse_state = 1;
523 } else {
524 mouse_state = 0;
526 if (mouse_x != gps.mouse_x || mouse_y != gps.mouse_y ||
527 mouse_state != enabler.tracking_on) {
528 // Pause rendering loop and update values
529 if (!paused_loop) {
530 pause_async_loop();
531 paused_loop = true;
533 enabler.tracking_on = mouse_state;
534 gps.mouse_x = mouse_x;
535 gps.mouse_y = mouse_y;
539 if (paused_loop)
540 unpause_async_loop();
542 do_frame();
543 #if !defined(NO_FMOD)
544 // Call FMOD::System.update(). Manages a bunch of sound stuff.
545 musicsound.update();
546 #endif
550 int enablerst::loop(string cmdline) {
551 command_line = cmdline;
553 // Initialize the tick counters
554 simticks.write(0);
555 gputicks.write(0);
557 // Call DF's initialization routine
558 if (!beginroutine())
559 exit(EXIT_FAILURE);
561 // Allocate a renderer
562 if (init.display.flag.has_flag(INIT_DISPLAY_FLAG_TEXT)) {
563 #ifdef CURSES
564 renderer = new renderer_curses();
565 #else
566 report_error("PRINT_MODE", "TEXT not supported on windows");
567 exit(EXIT_FAILURE);
568 #endif
569 } else if (init.display.flag.has_flag(INIT_DISPLAY_FLAG_2D)) {
570 renderer = new renderer_2d();
571 #ifdef WANT_GL
572 } else if (init.display.flag.has_flag(INIT_DISPLAY_FLAG_ACCUM_BUFFER)) {
573 renderer = new renderer_accum_buffer();
574 } else if (init.display.flag.has_flag(INIT_DISPLAY_FLAG_FRAME_BUFFER)) {
575 renderer = new renderer_framebuffer();
576 } else if (init.display.flag.has_flag(INIT_DISPLAY_FLAG_PARTIAL_PRINT)) {
577 if (init.display.partial_print_count)
578 renderer = new renderer_partial();
579 else
580 renderer = new renderer_once();
581 } else if (init.display.flag.has_flag(INIT_DISPLAY_FLAG_VBO)) {
582 renderer = new renderer_vbo();
583 } else {
584 renderer = new renderer_opengl();
585 #else
586 renderer = new renderer_2d();
587 #endif
590 // At this point we should have a window that is setup to render DF.
591 if (init.display.flag.has_flag(INIT_DISPLAY_FLAG_TEXT)) {
592 #ifdef CURSES
593 eventLoop_ncurses();
594 #endif
595 } else {
596 SDL_EnableUNICODE(1);
597 eventLoop_SDL();
600 endroutine();
602 // Clean up graphical resources
603 delete renderer;
606 void enablerst::override_grid_size(int x, int y) {
607 if (SDL_ThreadID() != renderer_threadid) {
608 // Ask the renderer to do it
609 async_msg m(async_msg::push_resize);
610 m.x = x; m.y = y;
611 async_frombox.write(m);
612 async_fromcomplete.read();
613 } else {
614 // We are the renderer; do it.
615 overridden_grid_sizes.push(make_pair(init.display.grid_x,init.display.grid_y));
616 renderer->grid_resize(x, y);
620 void enablerst::release_grid_size() {
621 if (SDL_ThreadID() != renderer_threadid) {
622 async_frombox.write(async_msg(async_msg::pop_resize));
623 async_fromcomplete.read();
624 } else {
625 if (!overridden_grid_sizes.size()) return;
626 // FIXME: Find out whatever is causing release to be called too rarely; right now
627 // we're overriding once per movie but apparently only releasing for the last one.
628 pair<int,int> sz;
629 while (overridden_grid_sizes.size()) {
630 sz = overridden_grid_sizes.top();
631 overridden_grid_sizes.pop();
633 zoom_display(zoom_resetgrid);
637 void enablerst::zoom_display(zoom_commands command) {
638 async_zoom.write(command);
641 int enablerst::calculate_fps() {
642 if (frame_timings.size() < 50)
643 return get_fps();
644 else
645 return calculated_fps;
647 int enablerst::calculate_gfps() {
648 if (gframe_timings.size() < 50)
649 return get_gfps();
650 else
651 return calculated_gfps;
654 void enablerst::do_update_fps(queue<int> &q, int &sum, int &last, int &calc) {
655 while (q.size() > 50 && sum > 10000) {
656 sum -= q.front();
657 q.pop();
659 const int now = SDL_GetTicks();
660 const int interval = now - last;
661 q.push(interval);
662 sum += interval;
663 last = now;
664 if (sum)
665 calc = q.size() * 1000 / sum;
668 void enablerst::clear_fps() {
669 while (frame_timings.size())
670 frame_timings.pop();
671 frame_sum = 0;
672 frame_last = SDL_GetTicks();
673 calculated_fps = get_fps();
676 void enablerst::update_fps() {
677 do_update_fps(frame_timings, frame_sum, frame_last, calculated_fps);
680 void enablerst::update_gfps() {
681 do_update_fps(gframe_timings, gframe_sum, gframe_last, calculated_gfps);
684 void enablerst::set_fps(int fps) {
685 if (SDL_ThreadID() != renderer_threadid) {
686 async_msg m(async_msg::set_fps);
687 m.fps = fps;
688 async_paused = true;
689 async_frombox.write(m);
690 async_fromcomplete.read();
691 } else {
692 if (fps == 0)
693 fps = 1048576;
694 this->fps = fps;
695 fps_per_gfps = fps / gfps;
696 struct async_cmd cmd;
697 cmd.cmd = async_cmd::set_fps;
698 cmd.val = fps;
699 async_tobox.write(cmd);
700 async_tobox.write(async_cmd(async_cmd::start));
704 void enablerst::set_gfps(int gfps) {
705 if (SDL_ThreadID() != renderer_threadid) {
706 async_msg m(async_msg::set_gfps);
707 m.fps = gfps;
708 async_frombox.write(m);
709 async_fromcomplete.read();
710 } else {
711 if (gfps == 0)
712 gfps = 50;
713 this->gfps = gfps;
714 fps_per_gfps = fps / gfps;
718 int call_loop(void *dummy) {
719 enabler.async_loop();
720 return 0;
723 int main (int argc, char* argv[]) {
724 #ifdef unix
725 setlocale(LC_ALL, "");
726 #endif
727 #if !defined(__APPLE__) && defined(unix) && defined(HAVE_GTK2)
728 bool gtk_ok = false;
729 if (getenv("DISPLAY"))
730 gtk_ok = gtk_init_check(&argc, &argv);
731 #endif
733 // Initialise minimal SDL subsystems.
734 int retval = SDL_Init(SDL_INIT_TIMER);
735 // Report failure?
736 if (retval != 0) {
737 report_error("SDL initialization failure", SDL_GetError());
738 return false;
740 enabler.renderer_threadid = SDL_ThreadID();
742 // Spawn simulation thread
743 SDL_CreateThread(call_loop, NULL);
745 init.begin(); // Load init.txt settings
747 #if !defined(__APPLE__) && defined(unix) && defined(HAVE_GTK2)
748 if (!gtk_ok && !init.display.flag.has_flag(INIT_DISPLAY_FLAG_TEXT)) {
749 puts("Display not found and PRINT_MODE not set to TEXT, aborting.");
750 exit(EXIT_FAILURE);
752 if (init.display.flag.has_flag(INIT_DISPLAY_FLAG_TEXT) &&
753 init.display.flag.has_flag(INIT_DISPLAY_FLAG_USE_GRAPHICS)) {
754 puts("Graphical tiles are not compatible with text output, sorry");
755 exit(EXIT_FAILURE);
757 #endif
759 // Initialize video, if we /use/ video
760 retval = SDL_InitSubSystem(init.display.flag.has_flag(INIT_DISPLAY_FLAG_TEXT) ? 0 : SDL_INIT_VIDEO);
761 if (retval != 0) {
762 report_error("SDL initialization failure", SDL_GetError());
763 return false;
766 #ifdef linux
767 if (!init.media.flag.has_flag(INIT_MEDIA_FLAG_SOUND_OFF)) {
768 // Initialize OpenAL
769 if (!musicsound.initsound()) {
770 puts("Initializing OpenAL failed, no sound will be played");
771 init.media.flag.add_flag(INIT_MEDIA_FLAG_SOUND_OFF);
774 #endif
776 #ifdef WIN32
777 // Attempt to get as good a timer as possible
778 int ms = 1;
779 while (timeBeginPeriod(ms) != TIMERR_NOERROR) ms++;
780 #endif
782 // Load keyboard map
783 keybinding_init();
784 enabler.load_keybindings("data/init/interface.txt");
786 string cmdLine;
787 for (int i = 1; i < argc; ++i) {
788 char *option = argv[i];
789 string opt=option;
790 if(opt.length()>=1)
792 //main removes quotes, unlike the winmain version, so it has to be rebuilt
793 if(opt[0]=='-')
795 cmdLine += opt;
796 cmdLine += " ";
798 else
800 cmdLine += "\"";
801 cmdLine += opt;
802 cmdLine += "\"";
803 cmdLine += " ";
807 int result = enabler.loop(cmdLine);
809 SDL_Quit();
811 #ifdef WIN32
812 timeEndPeriod(ms);
813 #endif
815 return result;
818 char get_slot_and_addbit_uchar(unsigned char &addbit,long &slot,long checkflag,long slotnum)
820 if(checkflag<0)return 0;
822 //FIND PROPER SLOT
823 slot=checkflag>>3;
824 if(slot>=slotnum)return 0;
826 //FIND PROPER BIT IN THAT SLOT
827 addbit=1<<(checkflag%8);
829 return 1;
832 void text_system_file_infost::initialize_info()
834 std::ifstream fseed(filename.c_str());
835 if(fseed.is_open())
837 string str;
839 while(std::getline(fseed,str))
841 if(str.length()>0)number++;
844 else
846 string str;
847 str="Error Initializing Text: ";
848 str+=filename;
849 errorlog_string(str);
851 fseed.close();
854 void text_system_file_infost::get_text(text_infost &text)
856 text.clean();
858 if(number==0)return;
860 std::ifstream fseed(filename.c_str());
861 if(fseed.is_open())
863 string str;
865 int num=trandom(number);
867 //SKIP AHEAD TO THE RIGHT SPOT
868 while(num>0)
870 std::getline(fseed,str);
871 num--;
874 //PROCESS THE STRING INTO TEXT ELEMENTS
875 if(std::getline(fseed,str))
877 int curpos;
878 string nextstr;
879 char doing_long=0;
881 text_info_elementst *newel;
882 long end=str.length();
884 while(end>0)
886 if(isspace(str[end-1]))end--;
887 else break;
890 str.resize(end);
892 for(curpos=0;curpos<end;curpos++)
894 //HANDLE TOKEN OR ENDING
895 //TWO FILE TOKENS IN A ROW MEANS LONG
896 //ONE MEANS STRING
897 if(str[curpos]==file_token || curpos==end-1)
899 if(str[curpos]!=file_token)nextstr+=str[curpos];
901 //HAVE SOMETHING == SAVE IT
902 if(!nextstr.empty())
904 if(doing_long)
906 newel=new text_info_element_longst(atoi(nextstr.c_str()));
907 text.element.push_back(newel);
908 doing_long=0;
910 else
912 newel=new text_info_element_stringst(nextstr);
913 text.element.push_back(newel);
916 nextstr.erase();
918 //STARTING A LONG
919 else
921 doing_long=1;
924 //JUST ADD IN ANYTHING ELSE
925 else
927 nextstr+=str[curpos];
932 fseed.close();
935 void curses_text_boxst::add_paragraph(const string &src,int32_t para_width)
937 stringvectst sp;
938 sp.add_string(src);
939 add_paragraph(sp,para_width);
942 void curses_text_boxst::add_paragraph(stringvectst &src,int32_t para_width)
944 bool skip_leading_spaces=false;
946 //ADD EACH OF THE STRINGS ON IN TURN
947 string curstr;
948 long strlength=0;
949 long s,pos;
950 for(s=0;s<src.str.size();s++)
952 //GRAB EACH WORD, AND SEE IF IT FITS, IF NOT START A NEW LINE
953 for(pos=0;pos<src.str[s]->dat.size();pos++)
955 if(skip_leading_spaces)
957 if(src.str[s]->dat[pos]==' ')continue;
958 else skip_leading_spaces=false;
961 //ADD TO WORD
962 curstr+=src.str[s]->dat[pos];
964 //IF TOO LONG, CUT BACK TO FIRST SPACE
965 if(curstr.length()>para_width)
967 long opos=pos;
969 long minus=0;
972 pos--;
973 minus++;
974 }while(src.str[s]->dat[pos]!=' '&&pos>0);
976 //IF WENT ALL THE WAY BACK, INTRODUCE A SPACE
977 if(minus==curstr.size())
979 src.str[s]->dat.insert(opos-1," ");
981 else
983 curstr.resize(curstr.size()-minus);
984 text.add_string(curstr);
985 skip_leading_spaces=true;
987 curstr.erase();
992 //FLUSH FINAL BIT
993 if(!curstr.empty())text.add_string(curstr);