1 /*
2  * Copyright © 2009 CNRS
3  * Copyright © 2009-2017 Inria.  All rights reserved.
4  * Copyright © 2009-2010, 2012 Université Bordeaux
5  * Copyright © 2011 Cisco Systems, Inc.  All rights reserved.
6  * See COPYING in top-level directory.
7  */
8 
9 #include <private/autogen/config.h>
10 #include <hwloc.h>
11 
12 #include <stdlib.h>
13 #include <stdio.h>
14 #ifdef HAVE_UNISTD_H
15 #include <unistd.h>
16 #endif
17 #include <string.h>
18 
19 #include <windows.h>
20 #include <windowsx.h>
21 
22 #include "lstopo.h"
23 
24 /* windows back-end.  */
25 
26 static struct color {
27   int r, g, b;
28   HGDIOBJ brush;
29 } *colors;
30 
31 struct lstopo_windows_output {
32   struct lstopo_output loutput; /* must be at the beginning */
33   int drawing;
34   PAINTSTRUCT ps;
35   HWND toplevel;
36   unsigned max_x;
37   unsigned max_y;
38 };
39 
40 static int numcolors;
41 
42 static HGDIOBJ
rgb_to_brush(int r,int g,int b)43 rgb_to_brush(int r, int g, int b)
44 {
45   int i;
46 
47   for (i = 0; i < numcolors; i++)
48     if (colors[i].r == r && colors[i].g == g && colors[i].b == b)
49       return colors[i].brush;
50 
51   fprintf(stderr, "color #%02x%02x%02x not declared\n", r, g, b);
52   exit(EXIT_FAILURE);
53 }
54 
55 struct draw_methods windows_draw_methods;
56 
57 static struct lstopo_windows_output the_output;
58 static int state, control;
59 static int the_x, the_y, x_delta, y_delta;
60 static int finish;
61 static int the_width, the_height;
62 static int win_width, win_height;
63 static unsigned int the_fontsize, the_gridsize;
64 static float the_scale;
65 
66 static void
67 windows_box(void *output, int r, int g, int b, unsigned depth __hwloc_attribute_unused, unsigned x, unsigned width, unsigned y, unsigned height);
68 
69 static LRESULT CALLBACK
WndProc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)70 WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
71 {
72   int redraw = 0;
73   switch (message) {
74     case WM_CHAR:  {
75       switch (wparam) {
76       case '+':
77 	the_scale *= 1.2f;
78 	redraw = 1;
79 	break;
80       case '-':
81 	the_scale /= 1.2f;
82 	redraw = 1;
83 	break;
84       case 'f':
85       case 'F': {
86 	float wscale, hscale;
87 	wscale = win_width / (float)the_width;
88 	hscale = win_height / (float)the_height;
89 	the_scale *= wscale > hscale ? hscale : wscale;
90 	redraw = 1;
91 	break;
92       }
93       case '1':
94 	the_scale = 1.0;
95 	redraw = 1;
96 	break;
97       case 'q':
98       case 'Q':
99 	finish = 1;
100 	break;
101       }
102       break;
103     }
104 
105     case WM_PAINT: {
106       HFONT font;
107       BeginPaint(hwnd, &the_output.ps);
108       font = CreateFont(fontsize, 0, 0, 0, 0, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, NULL);
109       SelectObject(the_output.ps.hdc, (HGDIOBJ) font);
110       SetBkMode(the_output.ps.hdc, TRANSPARENT);
111       windows_box(&the_output, 0xff, 0xff, 0xff, 0, 0, win_width, 0, win_height);
112       the_output.max_x = 0;
113       the_output.max_y = 0;
114       output_draw(&the_output.loutput);
115       the_width = the_output.max_x;
116       the_height = the_output.max_y;
117       DeleteObject(font);
118       EndPaint(hwnd, &the_output.ps);
119       break;
120     }
121     case WM_LBUTTONDOWN:
122       state = 1;
123       the_x = GET_X_LPARAM(lparam);
124       the_y = GET_Y_LPARAM(lparam);
125       break;
126     case WM_LBUTTONUP:
127       state = 0;
128       break;
129     case WM_MOUSEMOVE:
130       if (!(wparam & MK_LBUTTON))
131         state = 0;
132       if (state) {
133         int new_x = GET_X_LPARAM(lparam);
134         int new_y = GET_Y_LPARAM(lparam);
135         x_delta -= new_x - the_x;
136         y_delta -= new_y - the_y;
137         the_x = new_x;
138         the_y = new_y;
139         redraw = 1;
140       }
141       break;
142     case WM_KEYDOWN:
143       switch (wparam) {
144       case VK_ESCAPE:
145         finish = 1;
146         break;
147       case VK_LEFT:
148         x_delta -= win_width/10;
149         redraw = 1;
150         break;
151       case VK_RIGHT:
152         x_delta += win_width/10;
153         redraw = 1;
154         break;
155       case VK_UP:
156         y_delta -= win_height/10;
157         redraw = 1;
158         break;
159       case VK_DOWN:
160         y_delta += win_height/10;
161         redraw = 1;
162         break;
163       case VK_PRIOR:
164         if (control) {
165           x_delta -= win_width;
166           redraw = 1;
167         } else {
168           y_delta -= win_height;
169           redraw = 1;
170         }
171         break;
172       case VK_NEXT:
173         if (control) {
174           x_delta += win_width;
175           redraw = 1;
176         } else {
177           y_delta += win_height;
178           redraw = 1;
179         }
180         break;
181       case VK_HOME:
182         x_delta = 0;
183         y_delta = 0;
184         redraw = 1;
185         break;
186       case VK_END:
187         x_delta = INT_MAX;
188         y_delta = INT_MAX;
189         redraw = 1;
190         break;
191       case VK_CONTROL:
192         control = 1;
193         break;
194       }
195       break;
196     case WM_KEYUP:
197       switch (wparam) {
198       case VK_CONTROL:
199         control = 0;
200         break;
201       }
202       break;
203     case WM_DESTROY:
204       /* only kill the program if closing the actual toplevel, not the fake one */
205       if (hwnd == the_output.toplevel)
206 	PostQuitMessage(0);
207       return 0;
208     case WM_SIZE: {
209       float wscale, hscale;
210       win_width = LOWORD(lparam);
211       win_height = HIWORD(lparam);
212       wscale = win_width / (float)the_width;
213       hscale = win_height / (float)the_height;
214       the_scale *= wscale > hscale ? hscale : wscale;
215       if (the_scale < 1.0f)
216 	the_scale = 1.0f;
217       redraw = 1;
218       break;
219     }
220   }
221   if (redraw) {
222     if (x_delta > the_width - win_width)
223       x_delta = the_width - win_width;
224     if (y_delta > the_height - win_height)
225       y_delta = the_height - win_height;
226     if (x_delta < 0)
227       x_delta = 0;
228     if (y_delta < 0)
229       y_delta = 0;
230     fontsize = (unsigned)(the_fontsize * the_scale);
231     gridsize = (unsigned)(the_gridsize * the_scale);
232     RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE);
233   }
234   return DefWindowProc(hwnd, message, wparam, lparam);
235 }
236 
237 static void
windows_init(void * output)238 windows_init(void *output)
239 {
240   struct lstopo_windows_output *woutput = output;
241   WNDCLASS wndclass;
242   HWND toplevel, faketoplevel;
243   unsigned width, height;
244   HFONT font;
245 
246   /* make sure WM_DESTROY on the faketoplevel won't kill the program */
247   woutput->toplevel = NULL;
248 
249   /* create the toplevel window, with random size for now */
250   memset(&wndclass, 0, sizeof(wndclass));
251   wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
252   wndclass.hCursor = LoadCursor(NULL, IDC_SIZEALL);
253   wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
254   wndclass.lpfnWndProc = WndProc;
255   wndclass.lpszClassName = "lstopo";
256 
257   RegisterClass(&wndclass);
258 
259   /* compute the maximal needed size, this may require the toplevel window in the future */
260   woutput->max_x = 0;
261   woutput->max_y = 0;
262   woutput->drawing = 0;
263   faketoplevel = CreateWindow("lstopo", "lstopo", WS_OVERLAPPEDWINDOW,
264 			      CW_USEDEFAULT, CW_USEDEFAULT,
265 			      10, 10, NULL, NULL, NULL, NULL);
266   BeginPaint(faketoplevel, &woutput->ps);
267   font = CreateFont(fontsize, 0, 0, 0, 0, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, NULL);
268   SelectObject(woutput->ps.hdc, (HGDIOBJ) font);
269   output_draw(&woutput->loutput);
270   DeleteObject(font);
271   EndPaint(faketoplevel, &woutput->ps);
272   DestroyWindow(faketoplevel);
273   woutput->drawing = 1;
274 
275   /* now create the actual toplevel with the sizes */
276   width = woutput->max_x;
277   height = woutput->max_y;
278 
279   win_width = width + 2*GetSystemMetrics(SM_CXSIZEFRAME);
280   win_height = height + 2*GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYCAPTION);
281 
282   if (win_width > GetSystemMetrics(SM_CXFULLSCREEN))
283     win_width = GetSystemMetrics(SM_CXFULLSCREEN);
284 
285   if (win_height > GetSystemMetrics(SM_CYFULLSCREEN))
286     win_height = GetSystemMetrics(SM_CYFULLSCREEN);
287 
288   toplevel = CreateWindow("lstopo", "lstopo", WS_OVERLAPPEDWINDOW,
289 		  CW_USEDEFAULT, CW_USEDEFAULT,
290 		  win_width, win_height, NULL, NULL, NULL, NULL);
291   woutput->toplevel = toplevel;
292 
293   the_width = width;
294   the_height = height;
295 
296   the_scale = 1.0f;
297 
298   the_fontsize = fontsize;
299   the_gridsize = gridsize;
300 
301   /* and display the window */
302   ShowWindow(toplevel, SW_SHOWDEFAULT);
303 
304   printf("\n");
305   printf("Keyboard shortcuts:\n");
306   printf(" Zoom-in or out .................... + -\n");
307   printf(" Try to fit scale to window ........ f F\n");
308   printf(" Reset scale to default ............ 1\n");
309   printf(" Scroll vertically ................. Up Down PageUp PageDown\n");
310   printf(" Scroll horizontally ............... Left Right Ctrl+PageUp/Down\n");
311   printf(" Scroll to the top-left corner ..... Home\n");
312   printf(" Scroll to the bottom-right corner . End\n");
313   printf(" Exit .............................. q Q Esc\n");
314   printf("\n\n");
315 }
316 
317 static void
windows_declare_color(void * output,int r,int g,int b)318 windows_declare_color(void *output, int r, int g, int b)
319 {
320   struct lstopo_windows_output *woutput = output;
321   HBRUSH brush;
322   COLORREF color;
323   struct color *tmp;
324 
325   if (!woutput->drawing)
326     return;
327 
328   color = RGB(r, g, b);
329   brush = CreateSolidBrush(color);
330   if (!brush) {
331     fprintf(stderr,"Could not allocate color %02x%02x%02x\n", r, g, b);
332     exit(EXIT_FAILURE);
333   }
334 
335   tmp = realloc(colors, sizeof(*colors) * (numcolors + 1));
336   if (!tmp) {
337     fprintf(stderr, "Failed to realloc the colors array\n");
338     return;
339   }
340   colors = tmp;
341   colors[numcolors].r = r;
342   colors[numcolors].g = g;
343   colors[numcolors].b = b;
344   colors[numcolors].brush = (HGDIOBJ) brush;
345   numcolors++;
346 }
347 
348 static void
windows_box(void * output,int r,int g,int b,unsigned depth __hwloc_attribute_unused,unsigned x,unsigned width,unsigned y,unsigned height)349 windows_box(void *output, int r, int g, int b, unsigned depth __hwloc_attribute_unused, unsigned x, unsigned width, unsigned y, unsigned height)
350 {
351   struct lstopo_windows_output *woutput = output;
352   PAINTSTRUCT *ps = &woutput->ps;
353 
354   if (x > woutput->max_x)
355     woutput->max_x = x;
356   if (x+width > woutput->max_x)
357     woutput->max_x = x + width;
358   if (y > woutput->max_y)
359     woutput->max_y = y;
360   if (y + height > woutput->max_y)
361     woutput->max_y = y + height;
362 
363   if (!woutput->drawing)
364     return;
365 
366   SelectObject(ps->hdc, rgb_to_brush(r, g, b));
367   SetBkColor(ps->hdc, RGB(r, g, b));
368   Rectangle(ps->hdc, x - x_delta, y - y_delta, x + width - x_delta, y + height - y_delta);
369 }
370 
371 static void
windows_line(void * output,int r,int g,int b,unsigned depth __hwloc_attribute_unused,unsigned x1,unsigned y1,unsigned x2,unsigned y2)372 windows_line(void *output, int r, int g, int b, unsigned depth __hwloc_attribute_unused, unsigned x1, unsigned y1, unsigned x2, unsigned y2)
373 {
374   struct lstopo_windows_output *woutput = output;
375   PAINTSTRUCT *ps = &woutput->ps;
376 
377   if (x1 > woutput->max_x)
378     woutput->max_x = x1;
379   if (x2 > woutput->max_x)
380     woutput->max_x = x2;
381   if (y1 > woutput->max_y)
382     woutput->max_y = y1;
383   if (y2 > woutput->max_y)
384     woutput->max_y = y2;
385 
386   if (!woutput->drawing)
387     return;
388 
389   SelectObject(ps->hdc, rgb_to_brush(r, g, b));
390   MoveToEx(ps->hdc, x1 - x_delta, y1 - y_delta, NULL);
391   LineTo(ps->hdc, x2 - x_delta, y2 - y_delta);
392 }
393 
394 static void
windows_text(void * output,int r,int g,int b,unsigned depth __hwloc_attribute_unused,unsigned x,unsigned y,const char * text)395 windows_text(void *output, int r, int g, int b, unsigned depth __hwloc_attribute_unused, unsigned x, unsigned y, const char *text)
396 {
397   struct lstopo_windows_output *woutput = output;
398   PAINTSTRUCT *ps = &woutput->ps;
399 
400   if (!woutput->drawing)
401     return;
402 
403   SetTextColor(ps->hdc, RGB(r, g, b));
404   TextOut(ps->hdc, x - x_delta, y - y_delta, text, (int)strlen(text));
405 }
406 
407 static void
windows_textsize(void * output,const char * text,unsigned textlength,unsigned * width)408 windows_textsize(void *output, const char *text, unsigned textlength, unsigned *width)
409 {
410   struct lstopo_windows_output *woutput = output;
411   PAINTSTRUCT *ps = &woutput->ps;
412   SIZE size;
413 
414   GetTextExtentPoint32(ps->hdc, text, textlength, &size);
415   *width = size.cx;
416 }
417 
418 struct draw_methods windows_draw_methods = {
419   windows_init,
420   windows_declare_color,
421   windows_box,
422   windows_line,
423   windows_text,
424   windows_textsize,
425 };
426 
427 void
output_windows(struct lstopo_output * loutput,const char * filename __hwloc_attribute_unused)428 output_windows (struct lstopo_output *loutput, const char *filename __hwloc_attribute_unused)
429 {
430   MSG msg;
431 
432   memset(&the_output, 0, sizeof(the_output));
433   memcpy(&the_output.loutput, loutput, sizeof(*loutput));
434   the_output.loutput.methods = &windows_draw_methods;
435 
436   output_draw_start(&the_output.loutput);
437   UpdateWindow(the_output.toplevel);
438   while (!finish && GetMessage(&msg, NULL, 0, 0)) {
439     TranslateMessage(&msg);
440     DispatchMessage(&msg);
441   }
442 }
443