Hermes2D  2.0
view_support.cpp
1 // This file is part of Hermes2D.
2 //
3 // Copyright 2009 Ivo Hanak <hanak@byte.cz>
4 // Copyright 2005-2008 Jakub Cerveny <jakub.cerveny@gmail.com>
5 // Copyright 2005-2008 Lenka Dubcova <dubcova@gmail.com>
6 // Copyright 2005-2008 Pavel Solin <solin@unr.edu>
7 //
8 // Hermes2D is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation, either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // Hermes2D is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with Hermes2D. If not, see <http://www.gnu.org/licenses/>.
20 
21 #ifndef NOGLUT
22 
23 #include <GL/glew.h>
24 #include <GL/freeglut.h>
25 #ifndef WIN32
26 #include <sys/time.h>
27 #include <unistd.h>
28 #endif
29 
30 #include <map>
31 #include <vector>
32 #include "global.h"
33 #include "view.h"
34 #include "view_support.h"
35 
36 namespace Hermes
37 {
38  namespace Hermes2D
39  {
40  namespace Views
41  {
43 
44  /* monitor */
45  ViewMonitor::ViewMonitor()
46  {
47  pthread_mutexattr_init(&mutex_attr);
48  pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);
49  pthread_mutex_init(&mutex, &mutex_attr);
50 
51  pthread_cond_init(&cond_keypress, NULL);
52  pthread_cond_init(&cond_close, NULL);
53  pthread_cond_init(&cond_drawing_finished, NULL);
54  pthread_cond_init(&cond_cross_thread_call, NULL);
55  }
56 
57  ViewMonitor::~ViewMonitor()
58  {
59  pthread_mutex_destroy(&mutex);
60  pthread_mutexattr_destroy(&mutex_attr);
61  pthread_cond_destroy(&cond_keypress);
62  pthread_cond_destroy(&cond_close);
63  pthread_cond_destroy(&cond_drawing_finished);
64  pthread_cond_destroy(&cond_cross_thread_call);
65  }
66 
67  /* forward definitions */
68  static bool glut_initialized = false;
69  static bool glew_initialized = false;
70 
71  /* threading */
72  struct ThreadInfo {
73  pthread_t thread;
74  bool should_quit;
75  ThreadInfo() : should_quit(false) {};
76  };
77 
78  static ThreadInfo* view_thread;
79 
80  typedef int (*CTC_FUNC)(void*);
81  static CTC_FUNC ctc_function = NULL;
82  static void* ctc_param;
83  static int ctc_result;
84  static std::map<int, View*> view_instances;
85 
86  struct ViewParams {
88  int x, y;
89  int width, height;
90  const char* title;
91  ViewParams(View* view, int x, int y, int width, int height, const char* title) : view(view), x(x), y(y), width(width), height(height), title(title) {};
92  };
93 
94  struct RemoveParams {
95  int view_id;
97  RemoveParams(int view_id, bool destroy_glut_window) : view_id(view_id), destroy_glut_window(destroy_glut_window) {};
98  };
99 
100  struct TitleParams {
101  int view_id;
102  const char* title;
103  TitleParams(int view_id, const char* title) : view_id(view_id), title(title) {};
104  };
105 
106  /* view thread function */
107  static void* view_thread_func(void* param)
108  {
109  ThreadInfo* thread_info = (ThreadInfo*)param;
110 
111  //initize GLUT
112  init_glut();
113 
114  //run message loop
115  while(1)
116  {
117  //handle glut messages
118  glutMainLoopEvent();
119 
120  //check whether to quit
121  if(thread_info->should_quit)
122  break;
123 
124  //handle CTC
125  view_sync.enter();
126  if(ctc_function != NULL)
127  {
128  ctc_result = ctc_function(ctc_param);
129  ctc_function = NULL;
131  }
132  view_sync.leave();
133 
134  //wait for 10 ms
135 #ifdef WIN32
136  Sleep(10);
137 #else
138  usleep(10*1000);
139 #endif
140  }
141 
142  //cleanup
143  shutdown_glut();
144 
145  //cleanup
146  delete thread_info;
147  return NULL;
148  }
149 
151  static bool need_safe_call() { return pthread_equal(view_thread->thread, pthread_self()) == 0; }
152 
154  static int call_in_thread(CTC_FUNC func, void* param)
155  {
156  //check whether the thread is running if not start it
157  view_sync.enter();
158  if(view_thread == NULL)
159  {
160  ThreadInfo* new_thread_info = NULL;
161  try { new_thread_info = new ThreadInfo(); }
162  catch(std::bad_alloc&) { throw Hermes::Exceptions::Exception("Failed to allocate structure for view thread"); }
163  int err = pthread_create(&new_thread_info->thread, NULL, view_thread_func, new_thread_info);
164  if(err)
165  {
166  delete new_thread_info;
167  throw Hermes::Exceptions::Exception("Failed to create main thread, error: %d", err);
168  }
169  view_thread = new_thread_info;
170  }
171  view_sync.leave();
172 
173  //make call
174  if(need_safe_call())
175  {
176  view_sync.enter();
177  ctc_function = func;
178  ctc_param = param;
180  int result = ctc_result;
181  view_sync.leave();
182  return result;
183  }
184  else
185  return func(param);
186  }
187 
189  static int set_view_title_in_thread(void* title_pars_ptr)
190  {
191  TitleParams& title_params = *((TitleParams*)title_pars_ptr);
192  std::map<int, View*>::iterator found_view = view_instances.find(title_params.view_id);
193  if(found_view == view_instances.end())
194  {
195  throw Exceptions::Exception("Settings title of a view that is not registered.");
196  return -1;
197  }
198 
199  //create GLUT window
200  glutSetWindow(title_params.view_id);
201  glutSetWindowTitle(title_params.title);
202 
203  return 0;
204  }
205 
207  int add_view_in_thread(void* view_pars_ptr)
208  {
209  ViewParams& view_params = *((ViewParams*)view_pars_ptr);
210 
211  //create GLUT window
212  glutInitWindowPosition(view_params.x, view_params.y);
213  glutInitWindowSize(view_params.width, view_params.height);
214  int view_id = glutCreateWindow(view_params.title);
215  glutSetWindowData(view_params.view);
216 
217  //initialize GLEW
218  GLenum err = glewInit();
219  if(err != GLEW_OK)
220  throw Exceptions::Exception("GLEW error: %s", glewGetErrorString(err));
221  glew_initialized = true;
222 
223  //register callbacks
224  glutDisplayFunc(on_display_stub);
225  glutReshapeFunc(on_reshape_stub);
226  glutMotionFunc(on_mouse_move_stub);
227  glutPassiveMotionFunc(on_mouse_move_stub);
228  glutMouseFunc(on_mouse_click_stub);
229  glutKeyboardFunc(on_key_down_stub);
230  glutSpecialFunc(on_special_key_stub);
231  glutEntryFunc(on_entry_stub);
232  glutCloseFunc(on_close_stub);
233 
234  //add to structures
235  view_instances[view_id] = view_params.view;
236 
237  //call handlers
238  view_params.view->on_create(view_id);
239 
240  return view_id;
241  }
242 
244  int remove_view_in_thread(void* remove_params_ptr)
245  {
246  RemoveParams& params = *(RemoveParams*)remove_params_ptr;
247  std::map<int, View*>::iterator found_view = view_instances.find(params.view_id);
248  if(found_view == view_instances.end())
249  {
250  throw Exceptions::Exception("Removing of a view that is not registered");
251  return -1;
252  }
253 
254  //destroy window if requested (it will not be requested when remove is called as a reaction to on_close)
255  if(params.destroy_glut_window)
256  {
257  //remove window from GLUT
258  glutSetWindow(params.view_id);
259  glutSetWindowData(NULL); //prevent stubs from being executed if there is still some message waiting for the window
260 
261  //call on-close event
262  found_view->second->on_close();
263 
264  //finish removal of window from GLUT
265  glutDestroyWindow(params.view_id);
266  }
267 
268  //remove from structures
269  view_instances.erase(found_view);
270 
271  //thread cleanup
272  if(view_instances.empty())
273  {
274  view_thread->should_quit = true;
275  view_thread = NULL;
276 
277  //signal all events
281  }
282 
283  return 0;
284  }
285 
287  static int refresh_view_in_thread(void* view_id_ptr)
288  {
289  int view_id = *((int*)view_id_ptr);
290  std::map<int, View*>::iterator found_view = view_instances.find(view_id);
291  if(found_view == view_instances.end())
292  throw Exceptions::Exception("Refreshing a view that is not registered");
293 
294  //redisplay
295  if(found_view != view_instances.end())
296  {
297  glutSetWindow(view_id);
298  glutPostRedisplay();
299  }
300 
301  return 0;
302  }
303 
304  /* GLUT */
305  static long double_click_delay_ms = 300;
306 
307 #define STUB_GET_VIEW() View* wnd = (View*)glutGetWindowData() /* retrieves view for the current GLUT callback */
308 #define STUB_CALL(__call) STUB_GET_VIEW(); if(wnd != NULL) __call; /* calls a method of a view for the current GLUT callbakc */
309 
310  void on_display_stub(void) { STUB_CALL( wnd->pre_display() ); }
311  void on_reshape_stub(int width, int height) { STUB_CALL( wnd->on_reshape(width, height) ); }
312  void on_mouse_move_stub(int x, int y) { STUB_CALL( wnd->on_mouse_move(x, y) ); }
313  void on_key_down_stub(unsigned char key, int x, int y) { STUB_CALL( wnd->on_key_down(key, x, y) ); }
314  void on_special_key_stub(int key, int x, int y) { STUB_CALL( wnd->on_special_key(key, x, y) ); }
315  void on_entry_stub(int state) { STUB_CALL( wnd->on_entry(state) ); }
316  void on_mouse_click_stub(int button, int state, int x, int y)
317  {
318  STUB_GET_VIEW();
319  if(wnd == NULL)
320  return;
321 
322  // emulate double-click messages
323  if(state == GLUT_DOWN)
324  {
325  static double last_tick = 0;
326  double tick = View::get_tick_count();
327  //if(tick < last_tick) //todo
328  if(tick - last_tick < double_click_delay_ms)
329  {
330  if(button == GLUT_LEFT_BUTTON)
331  wnd->on_left_mouse_double_click(x, y);
332  else if(button == GLUT_RIGHT_BUTTON)
333  wnd->on_right_mouse_double_click(x, y);
334  else
335  wnd->on_middle_mouse_double_click(x, y);
336 
337  last_tick = 0;
338  return;
339  }
340  last_tick = tick;
341  }
342 
343  // call proper click handler
344  if(button == GLUT_LEFT_BUTTON)
345  {
346  if(state == GLUT_DOWN)
347  wnd->on_left_mouse_down(x, y);
348  else
349  wnd->on_left_mouse_up(x, y);
350  }
351  else if(button == GLUT_RIGHT_BUTTON)
352  {
353  if(state == GLUT_DOWN)
354  wnd->on_right_mouse_down(x, y);
355  else
356  wnd->on_right_mouse_up(x, y);
357  }
358  else
359  {
360  if(state == GLUT_DOWN)
361  wnd->on_middle_mouse_down(x, y);
362  else
363  wnd->on_middle_mouse_up(x, y);
364  }
365  }
366  void on_close_stub()
367  {
368  STUB_GET_VIEW();
369  if(wnd == NULL)
370  return;
371 
372  //call callback
373  wnd->on_close();
374 
375  //remove view from system
376  view_sync.enter();
377  RemoveParams params(glutGetWindow(), false);
378  remove_view_in_thread(&params);
380  view_sync.leave();
381  }
382 
384  bool init_glut()
385  {
386  static int argc = 1;
387  static const char* argv[1] = { "x" };
388 
389  //prepare GLUT environment
390  if(!glut_initialized)
391  {
392  glutInit(&argc, (char**) argv);
393  glut_initialized = true;
394  }
395  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_ACCUM);
396  glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION);
397 
398  //obtain other parameters
399 #ifdef WIN32
400  double_click_delay_ms = GetDoubleClickTime();
401 #endif
402 
403  return true;
404  }
405 
408  {
409  return true;
410  }
411 
412  /* public functions */
413  void set_view_title(int view_id, const char* title)
414  {
415  TitleParams params(view_id, title);
416  call_in_thread(set_view_title_in_thread, &params);
417  }
418 
419  int add_view(View* view, int x, int y, int width, int height, const char* title)
420  {
421  ViewParams params(view, x, y, width, height, title);
422  return call_in_thread(add_view_in_thread, &params);
423  }
424 
425  void refresh_view(int view_id) { call_in_thread(refresh_view_in_thread, &view_id); }
426 
427  void remove_view(int view_id)
428  {
429  RemoveParams params(view_id, true);
430  call_in_thread(remove_view_in_thread, &params);
431  }
432 
433  void force_view_thread_shutdown()
434  {
435  pthread_t current_thread;
436  bool should_wait = false;
437 
438  view_sync.enter();
439 
440  //destroy all views
441  std::map<int, View*>::const_iterator iter = view_instances.begin();
442  while (iter != view_instances.end())
443  {
444  glutDestroyWindow(iter->first);
445  ++iter;
446  }
447  view_instances.clear();
448 
449  //tell thread to finish
450  if(view_thread != NULL)
451  {
452  current_thread = view_thread->thread;
453  view_thread->should_quit = true;
454  view_thread = NULL;
455  }
456  view_sync.leave();
457 
458  //wait for thread to finish
459  if(should_wait)
460  {
461  glew_initialized = false;
462  pthread_join(current_thread, NULL);
463  }
464  }
465 
466  void wait_for_all_views_close(const char* text)
467  {
468  pthread_t current_thread;
469  bool should_wait = false;
470 
471  //tell thread to finish
472  view_sync.enter();
473  if(view_thread != NULL)
474  {
475  current_thread = view_thread->thread;
476  should_wait = true;
477  }
478  view_sync.leave();
479 
480  //wait for thread to finish
481  if(should_wait)
482  {
483  fprintf(stdout, "%s", text); fflush(stdout);
484  pthread_join(current_thread, NULL);
485  }
486  }
487 
488  void wait_for_any_key(const char* text)
489  {
490  //wait for key
491  view_sync.enter();
492  if(view_thread != NULL)
493  {
494  fprintf(stdout, "%s", text); fflush(stdout);
496  }
497  view_sync.leave();
498  }
499  }
500  }
501 }
502 #endif