1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2017 University of California
4 //
5 // BOINC is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License
7 // as published by the Free Software Foundation,
8 // either version 3 of the License, or (at your option) any later version.
9 //
10 // BOINC is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 // See the GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
17 
18 // unix-specific graphics stuff
19 //
20 #include "config.h"
21 #include <cstdlib>
22 #include <cstdio>
23 #include <csetjmp>
24 #include <unistd.h>
25 #include <pthread.h>
26 #include <csignal>
27 #include <cstring>
28 #include "x_opengl.h"
29 
30 #include "app_ipc.h"
31 #include "filesys.h"
32 #include "str_replace.h"
33 #include "util.h"
34 
35 #include "boinc_gl.h"
36 #include "boinc_glut.h"
37 #include "boinc_api.h"
38 #include "graphics2.h"
39 #include "diagnostics.h"
40 
41 #define TIMER_INTERVAL_MSEC 30
42 
43 static int xpos = 100, ypos = 100, width = 600, height = 400;
44 static int clicked_button;
45 static int win=0;
46 static int checkparentcounter=0;
47 
48 #ifdef __APPLE__
49 
50 static bool need_show = false;
51 #endif
52 
53 bool fullscreen;
54 
boinc_close_window_and_quit(const char * p)55 void boinc_close_window_and_quit(const char* p) {
56     char buf[256];
57     fprintf(stderr, "%s Quitting: %s\n", boinc_msg_prefix(buf, sizeof(buf)), p);
58     exit(0);
59 }
60 
boinc_close_window_and_quit_aux()61 void boinc_close_window_and_quit_aux() {
62     boinc_close_window_and_quit("GLUT close");
63 }
64 
65 // This callback is invoked when a user presses a key.
66 //
keyboardD(unsigned char key,int,int)67 void keyboardD(unsigned char key, int /*x*/, int /*y*/) {
68     if (fullscreen) {
69         boinc_close_window_and_quit("key down");
70     } else {
71         boinc_app_key_press((int) key, 0);
72     }
73 }
74 
keyboardU(unsigned char key,int,int)75 void keyboardU(unsigned char key, int /*x*/, int /*y*/) {
76     if (fullscreen) {
77         boinc_close_window_and_quit("key up");
78     } else {
79         boinc_app_key_release((int) key, 0);
80     }
81 }
82 
mouse_click(int button,int state,int x,int y)83 void mouse_click(int button, int state, int x, int y){
84     clicked_button = button;
85     if (fullscreen) {
86         boinc_close_window_and_quit("mouse click");
87     } else {
88         if (state) {
89             boinc_app_mouse_button(x, y, button, false);
90         } else {
91             boinc_app_mouse_button(x, y, button, true);
92         }
93     }
94 }
95 
mouse_click_move(int x,int y)96 void mouse_click_move(int x, int y){
97     if (fullscreen) {
98         boinc_close_window_and_quit("mouse move");
99     } else if (clicked_button == 2){
100         boinc_app_mouse_move(x, y, false, false, true);
101     } else if (clicked_button == 1){
102         boinc_app_mouse_move(x, y, false, true, false);
103     } else if (clicked_button == 0){
104         boinc_app_mouse_move(x, y, true, false, false);
105     } else{
106         boinc_app_mouse_move(x, y, false, false, false);
107     }
108 }
109 
maybe_render()110 static void maybe_render() {
111     int new_xpos, new_ypos, new_width, new_height;
112     static int size_changed = 0;
113 
114     new_xpos = glutGet(GLUT_WINDOW_X);
115     new_ypos = glutGet(GLUT_WINDOW_Y);
116     new_width = glutGet(GLUT_WINDOW_WIDTH);
117     new_height = glutGet(GLUT_WINDOW_HEIGHT);
118 
119 
120     if (throttled_app_render(new_width, new_height, dtime())) {
121 #ifdef __APPLE__
122         if (UseSharedOffscreenBuffer()) {
123             return; // Don't try to send garbage to screen
124         }
125 #endif
126         glutSwapBuffers();
127         if (! fullscreen) {
128             // If user has changed window size, wait until it stops
129             // changing and then write the new dimensions to file
130             if ((new_xpos != xpos) || (new_ypos != ypos) ||
131                 (new_width != width) || (new_height != height)
132                 ) {
133                     size_changed = 1;
134                     xpos = new_xpos;
135                     ypos = new_ypos;
136                     width = new_width;
137                     height = new_height;
138                 } else {
139                     if (size_changed && (++size_changed > 10)) {
140                         size_changed = 0;
141                         FILE *f = boinc_fopen("gfx_info", "w");
142                         if (f) {
143                             // ToDo: change this to XML
144                             fprintf(f, "%d %d %d %d\n", xpos, ypos, width, height);
145                             fclose(f);
146                         }
147                     }
148                 }               // End if (new size != previous size) else
149             }                   // End if (! fullscreen)
150 #ifdef __APPLE__
151         MacGLUTFix(fullscreen);
152         if (need_show) {
153             glutShowWindow();
154             need_show = false;
155         }
156 #endif
157     }
158 }
159 
make_window(const char * title)160 static void make_window(const char* title) {
161     char window_title[256];
162     if (title) {
163         strlcpy(window_title, title, sizeof(window_title));
164     } else {
165         get_window_title(window_title, 256);
166     }
167 
168     win = glutCreateWindow(window_title);
169     glutReshapeFunc(app_graphics_resize);
170     glutKeyboardFunc(keyboardD);
171     glutKeyboardUpFunc(keyboardU);
172     glutMouseFunc(mouse_click);
173     glutMotionFunc(mouse_click_move);
174     glutDisplayFunc(maybe_render);
175     glEnable(GL_DEPTH_TEST);
176 
177     app_graphics_init();
178 
179 #ifdef __APPLE__
180     glutWMCloseFunc(boinc_close_window_and_quit_aux);   // Enable the window's close box
181     BringAppToFront();
182     // Show window only after a successful call to throttled_app_render();
183     // this avoids momentary display of old image when screensaver restarts
184     // which made image appear to "jump."
185     need_show = true;
186 #endif
187 
188     if (fullscreen)  {
189         glutFullScreen();
190     }
191 }
192 
boinc_glut_init(int * argc,char ** argv)193 static void boinc_glut_init(int *argc, char** argv) {
194     win = 0;
195 
196     FILE *f = boinc_fopen("gfx_info", "r");
197     if (f) {
198         // ToDo: change this to XML parsing
199         int n = fscanf(f, "%d %d %d %d\n", &xpos, &ypos, &width, &height);
200         fclose(f);
201         if (n != 4) {
202             fprintf(stderr, "failed to parse gfx_info");
203         }
204     }
205 
206     glutInit (argc, argv);
207     glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_ALPHA);
208     glutInitWindowPosition(xpos, ypos);
209     glutInitWindowSize(width, height);
210 }
211 
timer_handler(int)212 static void timer_handler(int) {
213     maybe_render();
214     // When running under a V5 client, the worker app launches the graphics app
215     // so this code kills the graphics when the worker application exits.
216     // Under a V6 client, the Manager or Screensaver launched the graphics app
217     // so this code kills the graphics when the Manager or Screensaver exits.
218     if (--checkparentcounter < 0) {
219         // Check approximately twice per second if parent process still running
220         checkparentcounter = 500 / TIMER_INTERVAL_MSEC;
221         if (getppid() == 1) {
222             // Quit graphics application if parent process no longer running
223             boinc_close_window_and_quit("parent dead");
224         }
225     }
226     glutTimerFunc(TIMER_INTERVAL_MSEC, timer_handler, 0);
227 }
228 
boinc_graphics_loop(int argc,char ** argv,const char * title)229 void boinc_graphics_loop(int argc, char** argv, const char* title) {
230     if (!diagnostics_is_initialized()) {
231         boinc_init_graphics_diagnostics(BOINC_DIAG_DEFAULTS);
232     }
233 
234 #ifdef __APPLE__
235     char dir [MAXPATHLEN];
236     getcwd(dir, MAXPATHLEN);
237 #endif
238     for (int i=1; i<argc; i++) {
239         if (!strcmp(argv[i], "--fullscreen")) {
240             fullscreen = true;
241         }
242     }
243     boinc_glut_init(&argc, argv);
244     make_window(title);
245     glutTimerFunc(TIMER_INTERVAL_MSEC, timer_handler, 0);
246 #ifdef __APPLE__
247     // Apparently glut changed our working directory in OS 10.3.9
248     chdir(dir);
249 #endif
250     glutMainLoop();
251 }
252 
253 #ifdef __APPLE__
254 
UseSharedOffscreenBuffer()255 bool UseSharedOffscreenBuffer() {
256     static bool alreadyTested = false;
257     static bool needSharedGfxBuffer = false;
258 
259 //return true;    // FOR TESTING ONLY
260     if (alreadyTested) {
261         return needSharedGfxBuffer;
262     }
263     alreadyTested = true;
264     if (fullscreen) {
265         SInt32 major = -1;
266         SInt32 minor = -1;
267         char vers[100], *p1 = NULL;
268         FILE *f;
269         vers[0] = '\0';
270         f = popen("sw_vers -productVersion", "r");
271         if (f) {
272             fscanf(f, "%s", vers);
273             pclose(f);
274         }
275         if (vers[0] == '\0') {
276             fprintf(stderr, "popen(\"sw_vers -productVersion\" failed\n");
277             fflush(stderr);
278             return false;
279         }
280         // Extract the major system version number
281         major = atoi(vers);
282         if (major > 10) {   // OS 11.0 or later
283             needSharedGfxBuffer = true;
284             return true;
285         }
286         // Extract the minor system version number
287         p1 = strchr(vers, '.');
288         minor = atoi(p1+1);
289         if (minor > 12) {   // OS 10.13 or later
290             needSharedGfxBuffer = true;
291             return true;
292         }
293     }
294     return false;
295 }
296 #endif
297