1 // Copyright (c) 2012- PPSSPP Project.
2 
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6 
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 // GNU General Public License 2.0 for more details.
11 
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14 
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17 
18 #include "Common/CommonWindows.h"
19 #include <WindowsX.h>
20 #include "Common/Math/lin/matrix4x4.h"
21 #include "Common/GPU/OpenGL/GLSLProgram.h"
22 #include "Common/GPU/OpenGL/GLFeatures.h"
23 #include "Common/Common.h"
24 #include "Common/Log.h"
25 #include "Windows/GEDebugger/SimpleGLWindow.h"
26 #include "Windows/W32Util/ContextMenu.h"
27 
28 const wchar_t *SimpleGLWindow::windowClass = L"SimpleGLWindow";
29 
30 using namespace Lin;
31 
RegisterClass()32 void SimpleGLWindow::RegisterClass() {
33 	WNDCLASSEX wndClass;
34 
35 	wndClass.cbSize         = sizeof(wndClass);
36 	wndClass.lpszClassName  = windowClass;
37 	wndClass.hInstance      = GetModuleHandle(0);
38 	wndClass.lpfnWndProc    = WndProc;
39 	wndClass.hCursor        = LoadCursor(NULL, IDC_ARROW);
40 	wndClass.hIcon          = 0;
41 	wndClass.lpszMenuName   = 0;
42 	wndClass.hbrBackground  = (HBRUSH)GetSysColorBrush(COLOR_WINDOW);
43 	wndClass.style          = CS_DBLCLKS;
44 	wndClass.cbClsExtra     = 0;
45 	wndClass.cbWndExtra     = sizeof(SimpleGLWindow *);
46 	wndClass.hIconSm        = 0;
47 
48 	RegisterClassEx(&wndClass);
49 }
50 
51 static const char tex_fs[] =
52 	"#ifdef GL_ES\n"
53 	"precision mediump float;\n"
54 	"#endif\n"
55 	"uniform sampler2D sampler0;\n"
56 	"varying vec2 v_texcoord0;\n"
57 	"void main() {\n"
58 	"	gl_FragColor = texture2D(sampler0, v_texcoord0);\n"
59 	"	gl_FragColor.a = clamp(gl_FragColor.a, 0.2, 1.0);\n"
60 	"}\n";
61 
62 static const char basic_vs[] =
63 	"#version 120\n"
64 	"attribute vec4 a_position;\n"
65 	"attribute vec2 a_texcoord0;\n"
66 	"uniform mat4 u_viewproj;\n"
67 	"varying vec2 v_texcoord0;\n"
68 	"void main() {\n"
69 	"  v_texcoord0 = a_texcoord0;\n"
70 	"  gl_Position = u_viewproj * a_position;\n"
71 	"}\n";
72 
SimpleGLWindow(HWND wnd)73 SimpleGLWindow::SimpleGLWindow(HWND wnd)
74 	: hWnd_(wnd) {
75 	SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR) this);
76 }
77 
~SimpleGLWindow()78 SimpleGLWindow::~SimpleGLWindow() {
79 	if (vao_ != 0) {
80 		glDeleteVertexArrays(1, &vao_);
81 	}
82 	if (drawProgram_ != nullptr) {
83 		glsl_destroy(drawProgram_);
84 	}
85 	if (tex_) {
86 		glDeleteTextures(1, &tex_);
87 		glDeleteTextures(1, &checker_);
88 	}
89 	delete [] reformatBuf_;
90 };
91 
Initialize(u32 flags)92 void SimpleGLWindow::Initialize(u32 flags) {
93 	RECT rect;
94 	GetWindowRect(hWnd_, &rect);
95 
96 	SetFlags(flags);
97 	SetupGL();
98 	ResizeGL(rect.right-rect.left,rect.bottom-rect.top);
99 	CreateProgram();
100 	GenerateChecker();
101 }
102 
SetupGL()103 void SimpleGLWindow::SetupGL() {
104 	int pixelFormat;
105 
106 	static PIXELFORMATDESCRIPTOR pfd = {0};
107 	pfd.nSize = sizeof(pfd);
108 	pfd.nVersion = 1;
109 	pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
110 	pfd.iPixelType = PFD_TYPE_RGBA;
111 	pfd.cColorBits = 32;
112 	pfd.cDepthBits = 16;
113 	pfd.iLayerType = PFD_MAIN_PLANE;
114 
115 #define ENFORCE(x, msg) { if (!(x)) { ERROR_LOG(COMMON, "SimpleGLWindow: %s (%08x)", msg, (uint32_t)GetLastError()); return; } }
116 
117 	ENFORCE(hDC_ = GetDC(hWnd_), "Unable to create DC.");
118 	ENFORCE(pixelFormat = ChoosePixelFormat(hDC_, &pfd), "Unable to match pixel format.");
119 	ENFORCE(SetPixelFormat(hDC_, pixelFormat, &pfd), "Unable to set pixel format.");
120 	ENFORCE(hGLRC_ = wglCreateContext(hDC_), "Unable to create GL context.");
121 	ENFORCE(wglMakeCurrent(hDC_, hGLRC_), "Unable to activate GL context.");
122 
123 	glewInit();
124 	valid_ = true;
125 }
126 
ResizeGL(int w,int h)127 void SimpleGLWindow::ResizeGL(int w, int h) {
128 	if (!valid_) {
129 		return;
130 	}
131 
132 	wglMakeCurrent(hDC_, hGLRC_);
133 
134 	glViewport(0, 0, w, h);
135 	glScissor(0, 0, w, h);
136 	glMatrixMode(GL_PROJECTION);
137 	glLoadIdentity();
138 	glOrtho(0.0f, w, h, 0.0f, -1.0f, 1.0f);
139 	glMatrixMode(GL_MODELVIEW);
140 	glLoadIdentity();
141 
142 	w_ = w;
143 	h_ = h;
144 }
145 
CreateProgram()146 void SimpleGLWindow::CreateProgram() {
147 	if (!valid_) {
148 		return;
149 	}
150 
151 	wglMakeCurrent(hDC_, hGLRC_);
152 
153 	drawProgram_ = glsl_create_source(basic_vs, tex_fs);
154 	glGenTextures(1, &tex_);
155 	glGenTextures(1, &checker_);
156 
157 	glsl_bind(drawProgram_);
158 	glUniform1i(drawProgram_->sampler0, 0);
159 	glsl_unbind();
160 
161 	if (gl_extensions.ARB_vertex_array_object) {
162 		glGenVertexArrays(1, &vao_);
163 		glBindVertexArray(vao_);
164 
165 		glGenBuffers(1, &ibuf_);
166 		glGenBuffers(1, &vbuf_);
167 
168 		const GLubyte indices[4] = {0, 1, 3, 2};
169 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf_);
170 		glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
171 
172 		glBindBuffer(GL_ARRAY_BUFFER, vbuf_);
173 	} else {
174 		vao_ = 0;
175 	}
176 
177 	glEnableVertexAttribArray(drawProgram_->a_position);
178 	glEnableVertexAttribArray(drawProgram_->a_texcoord0);
179 }
180 
GenerateChecker()181 void SimpleGLWindow::GenerateChecker() {
182 	if (!valid_) {
183 		return;
184 	}
185 
186 	const static u8 checkerboard[] = {
187 		255,255,255,255, 195,195,195,255,
188 		195,195,195,255, 255,255,255,255,
189 	};
190 
191 	wglMakeCurrent(hDC_, hGLRC_);
192 
193 	glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
194 	glBindTexture(GL_TEXTURE_2D, checker_);
195 	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, checkerboard);
196 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
197 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
198 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
199 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
200 }
201 
DrawChecker()202 void SimpleGLWindow::DrawChecker() {
203 	wglMakeCurrent(hDC_, hGLRC_);
204 
205 	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
206 	glClear(GL_COLOR_BUFFER_BIT);
207 
208 	glBindTexture(GL_TEXTURE_2D, checker_);
209 
210 	glDisable(GL_BLEND);
211 	glViewport(0, 0, w_, h_);
212 	glScissor(0, 0, w_, h_);
213 
214 	glsl_bind(drawProgram_);
215 
216 	float fw = (float)w_, fh = (float)h_;
217 	const float pos[12] = {0,0,0, fw,0,0, fw,fh,0, 0,fh,0};
218 	const float texCoords[8] = {0,fh/22, fw/22,fh/22, fw/22,0, 0,0};
219 	const GLubyte indices[4] = {0,1,3,2};
220 
221 	Matrix4x4 ortho;
222 	ortho.setOrtho(0, (float)w_, (float)h_, 0, -1, 1);
223 	glUniformMatrix4fv(drawProgram_->u_viewproj, 1, GL_FALSE, ortho.getReadPtr());
224 	if (vao_) {
225 		glBufferData(GL_ARRAY_BUFFER, sizeof(pos) + sizeof(texCoords), nullptr, GL_DYNAMIC_DRAW);
226 		glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(pos), pos);
227 		glBufferSubData(GL_ARRAY_BUFFER, sizeof(pos), sizeof(texCoords), texCoords);
228 		glVertexAttribPointer(drawProgram_->a_position, 3, GL_FLOAT, GL_FALSE, 12, 0);
229 		glVertexAttribPointer(drawProgram_->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, (const void *)sizeof(pos));
230 	} else {
231 		glVertexAttribPointer(drawProgram_->a_position, 3, GL_FLOAT, GL_FALSE, 12, pos);
232 		glVertexAttribPointer(drawProgram_->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, texCoords);
233 	}
234 	glActiveTexture(GL_TEXTURE0);
235 	glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, vao_ ? 0 : indices);
236 }
237 
Draw(const u8 * data,int w,int h,bool flipped,Format fmt)238 void SimpleGLWindow::Draw(const u8 *data, int w, int h, bool flipped, Format fmt) {
239 	wglMakeCurrent(hDC_, hGLRC_);
240 
241 	GLint components = GL_RGBA;
242 	GLint memComponents = 0;
243 	GLenum glfmt = GL_UNSIGNED_BYTE;
244 	const u8 *finalData = data;
245 	if (fmt == FORMAT_8888) {
246 		glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
247 		glfmt = GL_UNSIGNED_BYTE;
248 	} else if (fmt == FORMAT_8888_BGRA) {
249 		glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
250 		glfmt = GL_UNSIGNED_BYTE;
251 		memComponents = GL_BGRA;
252 	} else if (fmt == FORMAT_FLOAT) {
253 		glfmt = GL_FLOAT;
254 		components = GL_RED;
255 	} else if (fmt == FORMAT_FLOAT_DIV_256) {
256 		glfmt = GL_UNSIGNED_INT;
257 		components = GL_RED;
258 		finalData = Reformat(data, fmt, w * h);
259 	} else if (fmt == FORMAT_24BIT_8X) {
260 		glfmt = GL_UNSIGNED_INT;
261 		components = GL_RED;
262 		finalData = Reformat(data, fmt, w * h);
263 	} else if (fmt == FORMAT_24BIT_8X_DIV_256) {
264 		glfmt = GL_UNSIGNED_INT;
265 		components = GL_RED;
266 		finalData = Reformat(data, fmt, w * h);
267 	} else if (fmt == FORMAT_24X_8BIT) {
268 		glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
269 		glfmt = GL_UNSIGNED_BYTE;
270 		components = GL_RED;
271 		finalData = Reformat(data, fmt, w * h);
272 	} else {
273 		glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
274 		if (fmt == FORMAT_4444) {
275 			glfmt = GL_UNSIGNED_SHORT_4_4_4_4;
276 		} else if (fmt == FORMAT_5551) {
277 			glfmt = GL_UNSIGNED_SHORT_5_5_5_1;
278 		} else if (fmt == FORMAT_565) {
279 			glfmt = GL_UNSIGNED_SHORT_5_6_5;
280 			components = GL_RGB;
281 		} else if (fmt == FORMAT_4444_REV) {
282 			glfmt = GL_UNSIGNED_SHORT_4_4_4_4_REV;
283 		} else if (fmt == FORMAT_5551_REV) {
284 			glfmt = GL_UNSIGNED_SHORT_1_5_5_5_REV;
285 		} else if (fmt == FORMAT_565_REV) {
286 			glfmt = GL_UNSIGNED_SHORT_5_6_5_REV;
287 			components = GL_RGB;
288 		} else if (fmt == FORMAT_5551_BGRA_REV) {
289 			glfmt = GL_UNSIGNED_SHORT_1_5_5_5_REV;
290 			memComponents = GL_BGRA;
291 		} else if (fmt == FORMAT_4444_BGRA_REV) {
292 			glfmt = GL_UNSIGNED_SHORT_4_4_4_4_REV;
293 			memComponents = GL_BGRA;
294 		} else if (fmt == FORMAT_16BIT) {
295 			glfmt = GL_UNSIGNED_SHORT;
296 			components = GL_RED;
297 		} else if (fmt == FORMAT_8BIT) {
298 			glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
299 			glfmt = GL_UNSIGNED_BYTE;
300 			components = GL_RED;
301 		} else {
302 			_dbg_assert_msg_(false, "Invalid SimpleGLWindow format.");
303 		}
304 	}
305 
306 	if (memComponents == 0) {
307 		memComponents = components;
308 	}
309 
310 	glBindTexture(GL_TEXTURE_2D, tex_);
311 	glTexImage2D(GL_TEXTURE_2D, 0, components, w, h, 0, memComponents, glfmt, finalData);
312 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
313 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
314 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
315 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
316 
317 	// Reset offset when the texture size changes.
318 	if (tw_ != w || th_ != h) {
319 		tw_ = w;
320 		th_ = h;
321 		offsetX_ = 0;
322 		offsetY_ = 0;
323 	}
324 	tflipped_ = flipped;
325 
326 	Redraw();
327 }
328 
GetContentSize(float & x,float & y,float & fw,float & fh)329 void SimpleGLWindow::GetContentSize(float &x, float &y, float &fw, float &fh) {
330 	fw = (float)tw_;
331 	fh = (float)th_;
332 	x = 0.0f;
333 	y = 0.0f;
334 
335 	if ((flags_ & RESIZE_SHRINK_FIT) != 0 && !zoom_) {
336 		float wscale = fw / w_, hscale = fh / h_;
337 
338 		// Too wide, and width is the biggest problem, so scale based on that.
339 		if (wscale > 1.0f && wscale > hscale) {
340 			fw = (float)w_;
341 			fh /= wscale;
342 		} else if (hscale > 1.0f) {
343 			fw /= hscale;
344 			fh = (float)h_;
345 		}
346 	}
347 	if ((flags_ & RESIZE_GROW_FIT) != 0 && !zoom_) {
348 		float wscale = fw / w_, hscale = fh / h_;
349 
350 		if (wscale > hscale && wscale < 1.0f) {
351 			fw = (float)w_;
352 			fh /= wscale;
353 		} else if (hscale > wscale && hscale < 1.0f) {
354 			fw /= hscale;
355 			fh = (float)h_;
356 		}
357 	}
358 	if (flags_ & RESIZE_CENTER) {
359 		x = ((float)w_ - fw) / 2;
360 		y = ((float)h_ - fh) / 2;
361 	}
362 
363 	x += offsetX_;
364 	y += offsetY_;
365 }
366 
Redraw(bool andSwap)367 void SimpleGLWindow::Redraw(bool andSwap) {
368 	DrawChecker();
369 
370 	auto swapWithCallback = [andSwap, this]() {
371 		if (andSwap) {
372 			swapped_ = false;
373 			if (redrawCallback_ && !inRedrawCallback_) {
374 				inRedrawCallback_ = true;
375 				redrawCallback_();
376 				inRedrawCallback_ = false;
377 			}
378 			// In case the callback swaps, don't do it twice.
379 			if (!swapped_) {
380 				Swap();
381 			}
382 		}
383 	};
384 
385 	if (tw_ == 0 && th_ == 0) {
386 		swapWithCallback();
387 		return;
388 	}
389 
390 	if (flags_ & ALPHA_BLEND) {
391 		glEnable(GL_BLEND);
392 		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
393 		glBlendEquation(GL_FUNC_ADD);
394 	} else {
395 		glDisable(GL_BLEND);
396 	}
397 	glViewport(0, 0, w_, h_);
398 	glScissor(0, 0, w_, h_);
399 
400 	glBindTexture(GL_TEXTURE_2D, tex_);
401 	glsl_bind(drawProgram_);
402 
403 	float fw, fh;
404 	float x, y;
405 	GetContentSize(x, y, fw, fh);
406 
407 	const float pos[12] = {x,y,0, x+fw,y,0, x+fw,y+fh,0, x,y+fh,0};
408 	static const float texCoordsNormal[8] = {0,0, 1,0, 1,1, 0,1};
409 	static const float texCoordsFlipped[8] = {0,1, 1,1, 1,0, 0,0};
410 	static const GLubyte indices[4] = {0,1,3,2};
411 	const float *texCoords = tflipped_ ? texCoordsFlipped : texCoordsNormal;
412 
413 	Matrix4x4 ortho;
414 	ortho.setOrtho(0, (float)w_, (float)h_, 0, -1, 1);
415 	glUniformMatrix4fv(drawProgram_->u_viewproj, 1, GL_FALSE, ortho.getReadPtr());
416 	if (vao_) {
417 		glBufferData(GL_ARRAY_BUFFER, sizeof(pos) + sizeof(texCoordsNormal), nullptr, GL_DYNAMIC_DRAW);
418 		glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(pos), pos);
419 		glBufferSubData(GL_ARRAY_BUFFER, sizeof(pos), sizeof(texCoordsNormal), texCoords);
420 		glVertexAttribPointer(drawProgram_->a_position, 3, GL_FLOAT, GL_FALSE, 12, 0);
421 		glVertexAttribPointer(drawProgram_->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, (const void *)sizeof(pos));
422 	} else {
423 		glVertexAttribPointer(drawProgram_->a_position, 3, GL_FLOAT, GL_FALSE, 12, pos);
424 		glVertexAttribPointer(drawProgram_->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, texCoords);
425 	}
426 	glActiveTexture(GL_TEXTURE0);
427 	glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, vao_ ? 0 : indices);
428 
429 	if (andSwap) {
430 		swapWithCallback();
431 	}
432 }
433 
Clear()434 void SimpleGLWindow::Clear() {
435 	tw_ = 0;
436 	th_ = 0;
437 	Redraw();
438 }
439 
Begin()440 void SimpleGLWindow::Begin() {
441 	if (!inRedrawCallback_) {
442 		Redraw(false);
443 	}
444 
445 	if (vao_) {
446 		glBindVertexArray(0);
447 		glBindBuffer(GL_ARRAY_BUFFER, 0);
448 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
449 	} else {
450 		glDisableVertexAttribArray(drawProgram_->a_position);
451 		glDisableVertexAttribArray(drawProgram_->a_texcoord0);
452 	}
453 }
454 
End()455 void SimpleGLWindow::End() {
456 	if (vao_) {
457 		glBindVertexArray(vao_);
458 		glBindBuffer(GL_ARRAY_BUFFER, vbuf_);
459 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf_);
460 	} else {
461 		glEnableVertexAttribArray(drawProgram_->a_position);
462 		glEnableVertexAttribArray(drawProgram_->a_texcoord0);
463 	}
464 
465 	Swap();
466 }
467 
DragStart(int mouseX,int mouseY)468 bool SimpleGLWindow::DragStart(int mouseX, int mouseY) {
469 	// Only while zoomed in, otherwise it's shrink to fit mode or fixed.
470 	if (!zoom_) {
471 		return false;
472 	}
473 
474 	dragging_ = true;
475 	SetCapture(hWnd_);
476 	dragStartX_ = mouseX - offsetX_;
477 	dragStartY_ = mouseY - offsetY_;
478 	dragLastUpdate_ = GetTickCount();
479 
480 	return true;
481 }
482 
DragContinue(int mouseX,int mouseY)483 bool SimpleGLWindow::DragContinue(int mouseX, int mouseY) {
484 	if (!dragging_) {
485 		return false;
486 	}
487 
488 	offsetX_ = mouseX - dragStartX_;
489 	offsetY_ = mouseY - dragStartY_;
490 
491 	const u32 MS_BETWEEN_DRAG_REDRAWS = 5;
492 	if (GetTickCount() - dragLastUpdate_ > MS_BETWEEN_DRAG_REDRAWS) {
493 		Redraw();
494 	}
495 
496 	return true;
497 }
498 
DragEnd(int mouseX,int mouseY)499 bool SimpleGLWindow::DragEnd(int mouseX, int mouseY) {
500 	if (!dragging_) {
501 		return false;
502 	}
503 
504 	dragging_ = false;
505 	ReleaseCapture();
506 	Redraw();
507 
508 	return true;
509 }
510 
ToggleZoom()511 bool SimpleGLWindow::ToggleZoom() {
512 	// Reset the offset when zooming out (or in, doesn't matter.)
513 	offsetX_ = 0;
514 	offsetY_ = 0;
515 
516 	zoom_ = !zoom_;
517 	Redraw();
518 
519 	return true;
520 }
521 
Hover(int mouseX,int mouseY)522 bool SimpleGLWindow::Hover(int mouseX, int mouseY) {
523 	if (hoverCallback_ == nullptr) {
524 		return false;
525 	}
526 
527 	float fw, fh;
528 	float x, y;
529 	GetContentSize(x, y, fw, fh);
530 
531 	if (mouseX < x || mouseX >= x + fw || mouseY < y || mouseY >= y + fh) {
532 		// Outside of bounds.
533 		hoverCallback_(-1, -1);
534 		return true;
535 	}
536 
537 	float tx = (mouseX - x) * (tw_ / fw);
538 	float ty = (mouseY - y) * (th_ / fh);
539 
540 	hoverCallback_((int)tx, (int)ty);
541 
542 	// Find out when they are done.
543 	TRACKMOUSEEVENT tracking = {0};
544 	tracking.cbSize = sizeof(tracking);
545 	tracking.dwFlags = TME_LEAVE;
546 	tracking.hwndTrack = hWnd_;
547 	TrackMouseEvent(&tracking);
548 
549 	return true;
550 }
551 
Leave()552 bool SimpleGLWindow::Leave() {
553 	if (hoverCallback_ == nullptr) {
554 		return false;
555 	}
556 
557 	hoverCallback_(-1, -1);
558 	return true;
559 }
560 
RightClick(int mouseX,int mouseY)561 bool SimpleGLWindow::RightClick(int mouseX, int mouseY) {
562 	if (rightClickCallback_ == nullptr) {
563 		return false;
564 	}
565 
566 	POINT pt{mouseX, mouseY};
567 
568 	rightClickCallback_(0);
569 	int result = TriggerContextMenu(rightClickMenu_, hWnd_, ContextPoint::FromClient(pt));
570 	if (result > 0) {
571 		rightClickCallback_(result);
572 	}
573 
574 	return true;
575 }
576 
Reformat(const u8 * data,Format fmt,u32 numPixels)577 const u8 *SimpleGLWindow::Reformat(const u8 *data, Format fmt, u32 numPixels) {
578 	if (!reformatBuf_ || reformatBufSize_ < numPixels) {
579 		delete [] reformatBuf_;
580 		reformatBuf_ = new u32[numPixels];
581 		reformatBufSize_ = numPixels;
582 	}
583 
584 	const u32 *data32 = (const u32 *)data;
585 	if (fmt == FORMAT_24BIT_8X) {
586 		for (u32 i = 0; i < numPixels; ++i) {
587 			reformatBuf_[i] = (data32[i] << 8) | ((data32[i] >> 16) & 0xFF);
588 		}
589 	} else if (fmt == FORMAT_24BIT_8X_DIV_256) {
590 		for (u32 i = 0; i < numPixels; ++i) {
591 			int z24 = data32[i] & 0x00FFFFFF;
592 			int z16 = z24 - 0x800000 + 0x8000;
593 			reformatBuf_[i] = (z16 << 16) | z16;
594 		}
595 	} else if (fmt == FORMAT_FLOAT_DIV_256) {
596 		for (u32 i = 0; i < numPixels; ++i) {
597 			double z = *(float *)&data32[i];
598 			int z24 = (int)(z * 16777215.0);
599 			int z16 = z24 - 0x800000 + 0x8000;
600 			reformatBuf_[i] = (z16 << 16) | z16;
601 		}
602 	} else if (fmt == FORMAT_24X_8BIT) {
603 		u8 *buf8 = (u8 *)reformatBuf_;
604 		for (u32 i = 0; i < numPixels; ++i) {
605 			u32 v = (data32[i] >> 24) & 0xFF;
606 			buf8[i] = v;
607 		}
608 	}
609 	return (const u8 *)reformatBuf_;
610 }
611 
GetFrom(HWND hwnd)612 SimpleGLWindow *SimpleGLWindow::GetFrom(HWND hwnd) {
613 	return (SimpleGLWindow*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
614 }
615 
WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)616 LRESULT CALLBACK SimpleGLWindow::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
617 	SimpleGLWindow *win = SimpleGLWindow::GetFrom(hwnd);
618 
619 	int mouseX = 0, mouseY = 0;
620 	switch (msg) {
621 	case WM_LBUTTONDOWN:
622 	case WM_LBUTTONUP:
623 	case WM_RBUTTONUP:
624 	case WM_MOUSEMOVE:
625 		mouseX = GET_X_LPARAM(lParam);
626 		mouseY = GET_Y_LPARAM(lParam);
627 		break;
628 	default:
629 		break;
630 	}
631 
632 	switch (msg) {
633 	case WM_NCCREATE:
634 		win = new SimpleGLWindow(hwnd);
635 
636 		// Continue with window creation.
637 		return win != nullptr ? TRUE : FALSE;
638 
639 	case WM_NCDESTROY:
640 		delete win;
641 		return 0;
642 
643 	case WM_LBUTTONDBLCLK:
644 		if (win->ToggleZoom()) {
645 			return 0;
646 		}
647 		break;
648 
649 	case WM_LBUTTONDOWN:
650 		if (win->DragStart(mouseX, mouseY)) {
651 			return 0;
652 		}
653 		break;
654 
655 	case WM_LBUTTONUP:
656 		if (win->DragEnd(mouseX, mouseY)) {
657 			return 0;
658 		}
659 		break;
660 
661 	case WM_MOUSEMOVE:
662 		if (win->DragContinue(mouseX, mouseY)) {
663 			return 0;
664 		}
665 		if (win->Hover(mouseX, mouseY)) {
666 			return 0;
667 		}
668 		break;
669 
670 	case WM_RBUTTONUP:
671 		if (win->RightClick(mouseX, mouseY)) {
672 			return 0;
673 		}
674 		break;
675 
676 	case WM_MOUSELEAVE:
677 		if (win->Leave()) {
678 			return 0;
679 		}
680 		break;
681 
682 	case WM_PAINT:
683 		win->Redraw();
684 		break;
685 	}
686 
687 	return DefWindowProc(hwnd, msg, wParam, lParam);
688 }
689