1/*
2 * Copyright 2006-2012, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Jérôme Duval, korli@users.berlios.de
7 *		Philippe Houdoin, philippe.houdoin@free.fr
8 *		Stefano Ceccherini, burton666@libero.it
9 */
10
11#include <kernel/image.h>
12
13#include <GLView.h>
14
15#include <assert.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19
20#include <DirectWindow.h>
21#include <GLRenderer.h>
22
23#include "interface/DirectWindowPrivate.h"
24#include "GLDispatcher.h"
25#include "GLRendererRoster.h"
26
27
28struct glview_direct_info {
29	direct_buffer_info* direct_info;
30	bool direct_connected;
31	bool enable_direct_mode;
32
33	glview_direct_info();
34	~glview_direct_info();
35};
36
37
38BGLView::BGLView(BRect rect, const char* name, ulong resizingMode, ulong mode,
39	ulong options)
40	:
41	BView(rect, name, B_FOLLOW_ALL_SIDES, mode | B_WILL_DRAW | B_FRAME_EVENTS),
42		//  | B_FULL_UPDATE_ON_RESIZE)
43	fGc(NULL),
44	fOptions(options),
45	fDitherCount(0),
46	fDrawLock("BGLView draw lock"),
47	fDisplayLock("BGLView display lock"),
48	fClipInfo(NULL),
49	fRenderer(NULL),
50	fRoster(NULL),
51	fDitherMap(NULL)
52{
53	fRoster = new GLRendererRoster(this, options);
54}
55
56
57BGLView::~BGLView()
58{
59	delete fClipInfo;
60	if (fRenderer)
61		fRenderer->Release();
62}
63
64
65void
66BGLView::LockGL()
67{
68	// TODO: acquire the OpenGL API lock it on this glview
69
70	fDisplayLock.Lock();
71	if (fRenderer)
72		fRenderer->LockGL();
73}
74
75
76void
77BGLView::UnlockGL()
78{
79	if (fRenderer)
80		fRenderer->UnlockGL();
81	fDisplayLock.Unlock();
82
83	// TODO: release the GL API lock to others glviews
84}
85
86
87void
88BGLView::SwapBuffers()
89{
90	SwapBuffers(false);
91}
92
93
94void
95BGLView::SwapBuffers(bool vSync)
96{
97	if (fRenderer) {
98		_LockDraw();
99		fRenderer->SwapBuffers(vSync);
100		_UnlockDraw();
101	}
102}
103
104
105BView*
106BGLView::EmbeddedView()
107{
108	return NULL;
109}
110
111
112void*
113BGLView::GetGLProcAddress(const char* procName)
114{
115	BGLDispatcher* glDispatcher = NULL;
116
117	if (fRenderer)
118		glDispatcher = fRenderer->GLDispatcher();
119
120	if (glDispatcher)
121		return (void*)glDispatcher->AddressOf(procName);
122
123	return NULL;
124}
125
126
127status_t
128BGLView::CopyPixelsOut(BPoint source, BBitmap* dest)
129{
130	if (!fRenderer)
131		return B_ERROR;
132
133	if (!dest || !dest->Bounds().IsValid())
134		return B_BAD_VALUE;
135
136	return fRenderer->CopyPixelsOut(source, dest);
137}
138
139
140status_t
141BGLView::CopyPixelsIn(BBitmap* source, BPoint dest)
142{
143	if (!fRenderer)
144		return B_ERROR;
145
146	if (!source || !source->Bounds().IsValid())
147		return B_BAD_VALUE;
148
149	return fRenderer->CopyPixelsIn(source, dest);
150}
151
152
153/*!	Mesa's GLenum is not ulong but uint, so we can't use GLenum
154	without breaking this method signature.
155	Instead, we have to use the effective BeOS's SGI OpenGL GLenum type:
156	unsigned long.
157 */
158void
159BGLView::ErrorCallback(unsigned long errorCode)
160{
161	char msg[32];
162	sprintf(msg, "GL: Error code $%04lx.", errorCode);
163	// TODO: under BeOS R5, it call debugger(msg);
164	fprintf(stderr, "%s\n", msg);
165}
166
167
168void
169BGLView::Draw(BRect updateRect)
170{
171	if (fRenderer) {
172		_LockDraw();
173		fRenderer->Draw(updateRect);
174		_UnlockDraw();
175		return;
176	}
177	// TODO: auto-size and center the string
178	MovePenTo(8, 32);
179	DrawString("No OpenGL renderer available!");
180}
181
182
183void
184BGLView::AttachedToWindow()
185{
186	BView::AttachedToWindow();
187
188	fBounds = Bounds();
189	for (BView* view = this; view != NULL; view = view->Parent())
190		view->ConvertToParent(&fBounds);
191
192	fRenderer = fRoster->GetRenderer();
193	if (fRenderer != NULL) {
194		// Jackburton: The following code was commented because it doesn't look
195		// good in "direct" mode:
196		// when the window is moved, the app_server doesn't paint the view's
197		// background, and the stuff behind the window itself shows up.
198		// Setting the view color to black, instead, looks a bit more elegant.
199#if 0
200		// Don't paint white window background when resized
201		SetViewColor(B_TRANSPARENT_32_BIT);
202#else
203		SetViewColor(0, 0, 0);
204#endif
205
206		// Set default OpenGL viewport:
207		LockGL();
208		glViewport(0, 0, Bounds().IntegerWidth(), Bounds().IntegerHeight());
209		UnlockGL();
210		fRenderer->FrameResized(Bounds().IntegerWidth(),
211			Bounds().IntegerHeight());
212
213		if (fClipInfo) {
214			fRenderer->DirectConnected(fClipInfo->direct_info);
215			fRenderer->EnableDirectMode(fClipInfo->enable_direct_mode);
216		}
217
218		return;
219	}
220
221	fprintf(stderr, "no renderer found! \n");
222
223	// No Renderer, no rendering. Setup a minimal "No Renderer" string drawing
224	// context
225	SetFont(be_bold_font);
226	// SetFontSize(16);
227}
228
229
230void
231BGLView::AllAttached()
232{
233	BView::AllAttached();
234}
235
236
237void
238BGLView::DetachedFromWindow()
239{
240	if (fRenderer)
241		fRenderer->Release();
242	fRenderer = NULL;
243
244	BView::DetachedFromWindow();
245}
246
247
248void
249BGLView::AllDetached()
250{
251	BView::AllDetached();
252}
253
254
255void
256BGLView::FrameResized(float width, float height)
257{
258	fBounds = Bounds();
259	for (BView* v = this; v; v = v->Parent())
260		v->ConvertToParent(&fBounds);
261
262	if (fRenderer) {
263		LockGL();
264		_LockDraw();
265		_CallDirectConnected();
266		fRenderer->FrameResized(width, height);
267		_UnlockDraw();
268		UnlockGL();
269	}
270
271	BView::FrameResized(width, height);
272}
273
274
275status_t
276BGLView::Perform(perform_code d, void* arg)
277{
278	return BView::Perform(d, arg);
279}
280
281
282status_t
283BGLView::Archive(BMessage* data, bool deep) const
284{
285	return BView::Archive(data, deep);
286}
287
288
289void
290BGLView::MessageReceived(BMessage* msg)
291{
292	BView::MessageReceived(msg);
293}
294
295
296void
297BGLView::SetResizingMode(uint32 mode)
298{
299	BView::SetResizingMode(mode);
300}
301
302
303void
304BGLView::GetPreferredSize(float* _width, float* _height)
305{
306	if (_width)
307		*_width = 0;
308	if (_height)
309		*_height = 0;
310}
311
312
313void
314BGLView::Show()
315{
316	BView::Show();
317}
318
319
320void
321BGLView::Hide()
322{
323	BView::Hide();
324}
325
326
327BHandler*
328BGLView::ResolveSpecifier(BMessage* msg, int32 index, BMessage* specifier,
329	int32 form, const char* property)
330{
331	return BView::ResolveSpecifier(msg, index, specifier, form, property);
332}
333
334
335status_t
336BGLView::GetSupportedSuites(BMessage* data)
337{
338	return BView::GetSupportedSuites(data);
339}
340
341
342void
343BGLView::DirectConnected(direct_buffer_info* info)
344{
345	if (fClipInfo == NULL) {
346		fClipInfo = new (std::nothrow) glview_direct_info();
347		if (fClipInfo == NULL)
348			return;
349	}
350
351	direct_buffer_info* localInfo = fClipInfo->direct_info;
352
353	switch (info->buffer_state & B_DIRECT_MODE_MASK) {
354		case B_DIRECT_START:
355			fClipInfo->direct_connected = true;
356			memcpy(localInfo, info, DIRECT_BUFFER_INFO_AREA_SIZE);
357			_UnlockDraw();
358			break;
359
360		case B_DIRECT_MODIFY:
361			_LockDraw();
362			memcpy(localInfo, info, DIRECT_BUFFER_INFO_AREA_SIZE);
363			_UnlockDraw();
364			break;
365
366		case B_DIRECT_STOP:
367			fClipInfo->direct_connected = false;
368			_LockDraw();
369			break;
370	}
371
372	if (fRenderer)
373		_CallDirectConnected();
374}
375
376
377void
378BGLView::EnableDirectMode(bool enabled)
379{
380	if (fRenderer)
381		fRenderer->EnableDirectMode(enabled);
382	if (fClipInfo == NULL) {
383		fClipInfo = new (std::nothrow) glview_direct_info();
384		if (fClipInfo == NULL)
385			return;
386	}
387
388	fClipInfo->enable_direct_mode = enabled;
389}
390
391
392void
393BGLView::_LockDraw()
394{
395	if (!fClipInfo || !fClipInfo->enable_direct_mode)
396		return;
397
398	fDrawLock.Lock();
399}
400
401
402void
403BGLView::_UnlockDraw()
404{
405	if (!fClipInfo || !fClipInfo->enable_direct_mode)
406		return;
407
408	fDrawLock.Unlock();
409}
410
411
412void
413BGLView::_CallDirectConnected()
414{
415	if (!fClipInfo)
416		return;
417
418	direct_buffer_info* localInfo = fClipInfo->direct_info;
419	direct_buffer_info* info = (direct_buffer_info*)malloc(
420		DIRECT_BUFFER_INFO_AREA_SIZE);
421	if (info == NULL)
422		return;
423
424	memcpy(info, localInfo, DIRECT_BUFFER_INFO_AREA_SIZE);
425
426	// Collect the rects into a BRegion, then clip to the view's bounds
427	BRegion region;
428	for (uint32 c = 0; c < localInfo->clip_list_count; c++)
429		region.Include(localInfo->clip_list[c]);
430	BRegion boundsRegion = fBounds.OffsetByCopy(localInfo->window_bounds.left,
431		localInfo->window_bounds.top);
432	info->window_bounds = boundsRegion.RectAtInt(0);
433		// window_bounds are now view bounds
434	region.IntersectWith(&boundsRegion);
435
436	info->clip_list_count = region.CountRects();
437	info->clip_bounds = region.FrameInt();
438
439	for (uint32 c = 0; c < info->clip_list_count; c++)
440		info->clip_list[c] = region.RectAtInt(c);
441	fRenderer->DirectConnected(info);
442	free(info);
443}
444
445
446//---- virtual reserved methods ----------
447
448
449void BGLView::_ReservedGLView1() {}
450void BGLView::_ReservedGLView2() {}
451void BGLView::_ReservedGLView3() {}
452void BGLView::_ReservedGLView4() {}
453void BGLView::_ReservedGLView5() {}
454void BGLView::_ReservedGLView6() {}
455void BGLView::_ReservedGLView7() {}
456void BGLView::_ReservedGLView8() {}
457
458
459// #pragma mark -
460
461
462// BeOS compatibility: contrary to others BView's contructors,
463// BGLView one wants a non-const name argument.
464BGLView::BGLView(BRect rect, char* name, ulong resizingMode, ulong mode,
465	ulong options)
466	:
467	BView(rect, name, B_FOLLOW_ALL_SIDES, mode | B_WILL_DRAW | B_FRAME_EVENTS),
468	fGc(NULL),
469	fOptions(options),
470	fDitherCount(0),
471	fDrawLock("BGLView draw lock"),
472	fDisplayLock("BGLView display lock"),
473	fClipInfo(NULL),
474	fRenderer(NULL),
475	fRoster(NULL),
476	fDitherMap(NULL)
477{
478	fRoster = new GLRendererRoster(this, options);
479}
480
481
482#if 0
483// TODO: implement BGLScreen class...
484
485
486BGLScreen::BGLScreen(char* name, ulong screenMode, ulong options,
487		status_t* error, bool debug)
488	:
489	BWindowScreen(name, screenMode, error, debug)
490{
491}
492
493
494BGLScreen::~BGLScreen()
495{
496}
497
498
499void
500BGLScreen::LockGL()
501{
502}
503
504
505void
506BGLScreen::UnlockGL()
507{
508}
509
510
511void
512BGLScreen::SwapBuffers()
513{
514}
515
516
517void
518BGLScreen::ErrorCallback(unsigned long errorCode)
519{
520	// Mesa's GLenum is not ulong but uint!
521	char msg[32];
522	sprintf(msg, "GL: Error code $%04lx.", errorCode);
523	// debugger(msg);
524	fprintf(stderr, "%s\n", msg);
525	return;
526}
527
528
529void
530BGLScreen::ScreenConnected(bool enabled)
531{
532}
533
534
535void
536BGLScreen::FrameResized(float width, float height)
537{
538	return BWindowScreen::FrameResized(width, height);
539}
540
541
542status_t
543BGLScreen::Perform(perform_code d, void* arg)
544{
545	return BWindowScreen::Perform(d, arg);
546}
547
548
549status_t
550BGLScreen::Archive(BMessage* data, bool deep) const
551{
552	return BWindowScreen::Archive(data, deep);
553}
554
555
556void
557BGLScreen::MessageReceived(BMessage* msg)
558{
559	BWindowScreen::MessageReceived(msg);
560}
561
562
563void
564BGLScreen::Show()
565{
566	BWindowScreen::Show();
567}
568
569
570void
571BGLScreen::Hide()
572{
573	BWindowScreen::Hide();
574}
575
576
577BHandler*
578BGLScreen::ResolveSpecifier(BMessage* msg, int32 index, BMessage* specifier,
579	int32 form, const char* property)
580{
581	return BWindowScreen::ResolveSpecifier(msg, index, specifier,
582		form, property);
583}
584
585
586status_t
587BGLScreen::GetSupportedSuites(BMessage* data)
588{
589	return BWindowScreen::GetSupportedSuites(data);
590}
591
592
593//---- virtual reserved methods ----------
594
595void BGLScreen::_ReservedGLScreen1() {}
596void BGLScreen::_ReservedGLScreen2() {}
597void BGLScreen::_ReservedGLScreen3() {}
598void BGLScreen::_ReservedGLScreen4() {}
599void BGLScreen::_ReservedGLScreen5() {}
600void BGLScreen::_ReservedGLScreen6() {}
601void BGLScreen::_ReservedGLScreen7() {}
602void BGLScreen::_ReservedGLScreen8() {}
603#endif
604
605
606const char* color_space_name(color_space space)
607{
608#define C2N(a)	case a:	return #a
609
610	switch (space) {
611	C2N(B_RGB24);
612	C2N(B_RGB32);
613	C2N(B_RGBA32);
614	C2N(B_RGB32_BIG);
615	C2N(B_RGBA32_BIG);
616	C2N(B_GRAY8);
617	C2N(B_GRAY1);
618	C2N(B_RGB16);
619	C2N(B_RGB15);
620	C2N(B_RGBA15);
621	C2N(B_CMAP8);
622	default:
623		return "Unknown!";
624	};
625
626#undef C2N
627};
628
629
630glview_direct_info::glview_direct_info()
631{
632	// TODO: See direct_window_data() in app_server's ServerWindow.cpp
633	direct_info = (direct_buffer_info*)calloc(1, DIRECT_BUFFER_INFO_AREA_SIZE);
634	direct_connected = false;
635	enable_direct_mode = false;
636}
637
638
639glview_direct_info::~glview_direct_info()
640{
641	free(direct_info);
642}
643
644