1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  * Port to the D Programming Language:
11  *     John Reimer <terminal.node@gmail.com>
12  *******************************************************************************/
13 
14 module org.eclipse.swt.opengl.GLCanvas;
15 
16 import java.lang.all;
17 
18 import org.eclipse.swt.SWT;
19 import org.eclipse.swt.SWTException;
20 import org.eclipse.swt.graphics.Rectangle;
21 
22 import org.eclipse.swt.internal.gtk.OS;
23 import org.eclipse.swt.internal.c.glx;
24 import org.eclipse.swt.internal.c.gdk   : GdkWindowAttr, GdkDrawable;
25 import org.eclipse.swt.internal.c.Xutil : XVisualInfo;
26 
27 import org.eclipse.swt.internal.opengl.glx.GLX;
28 
29 import org.eclipse.swt.widgets.Canvas;
30 import org.eclipse.swt.widgets.Composite;
31 import org.eclipse.swt.widgets.Event;
32 import org.eclipse.swt.widgets.Listener;
33 
34 import org.eclipse.swt.opengl.GLData;
35 
36 /**
37  * GLCanvas is a widget capable of displaying OpenGL content.
38  * 
39  * @see GLData
40  * @see <a href="http://www.eclipse.org/swt/snippets/#opengl">OpenGL snippets</a>
41  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
42  *
43  * @since 3.2
44  */
45 
46 public class GLCanvas : Canvas {
47     void* context;
48     size_t   xWindow;
49     GdkDrawable* glWindow;
50     XVisualInfo vinfo;
51     static const int MAX_ATTRIBUTES = 32;
52 
53 /**
54  * Create a GLCanvas widget using the attributes described in the GLData
55  * object provided.
56  *
57  * @param parent a composite widget
58  * @param style the bitwise OR'ing of widget styles
59  * @param data the requested attributes of the GLCanvas
60  *
61  * @exception IllegalArgumentException
62  * <ul><li>ERROR_NULL_ARGUMENT when the data is null
63  *     <li>ERROR_UNSUPPORTED_DEPTH when the requested attributes cannot be provided</ul> 
64  * </ul>
65  */
66 public this (Composite parent, int style, GLData data) {
67     super (parent, style);  
68     if (data is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
69     int[MAX_ATTRIBUTES] glxAttrib;
70     int pos = 0;
71     glxAttrib [pos++] = GLX.GLX_RGBA;
72     if (data.doubleBuffer) glxAttrib [pos++] = GLX.GLX_DOUBLEBUFFER;
73     if (data.stereo) glxAttrib [pos++] = GLX.GLX_STEREO;
74     if (data.redSize > 0) {
75         glxAttrib [pos++] = GLX.GLX_RED_SIZE;
76         glxAttrib [pos++] = data.redSize;
77     }
78     if (data.greenSize > 0) {
79         glxAttrib [pos++] = GLX.GLX_GREEN_SIZE;
80         glxAttrib [pos++] = data.greenSize;
81     }
82     if (data.blueSize > 0) {
83         glxAttrib [pos++] = GLX.GLX_BLUE_SIZE;
84         glxAttrib [pos++] = data.blueSize;
85     }
86     if (data.alphaSize > 0) {
87         glxAttrib [pos++] = GLX.GLX_ALPHA_SIZE;
88         glxAttrib [pos++] = data.alphaSize;
89     }
90     if (data.depthSize > 0) {
91         glxAttrib [pos++] = GLX.GLX_DEPTH_SIZE;
92         glxAttrib [pos++] = data.depthSize;
93     }
94     if (data.stencilSize > 0) {
95         glxAttrib [pos++] = GLX.GLX_STENCIL_SIZE;
96         glxAttrib [pos++] = data.stencilSize;
97     }
98     if (data.accumRedSize > 0) {
99         glxAttrib [pos++] = GLX.GLX_ACCUM_RED_SIZE;
100         glxAttrib [pos++] = data.accumRedSize;
101     }
102     if (data.accumGreenSize > 0) {
103         glxAttrib [pos++] = GLX.GLX_ACCUM_GREEN_SIZE;
104         glxAttrib [pos++] = data.accumGreenSize;
105     }
106     if (data.accumBlueSize > 0) {
107         glxAttrib [pos++] = GLX.GLX_ACCUM_BLUE_SIZE;
108         glxAttrib [pos++] = data.accumBlueSize;
109     }
110     if (data.accumAlphaSize > 0) {
111         glxAttrib [pos++] = GLX.GLX_ACCUM_ALPHA_SIZE;
112         glxAttrib [pos++] = data.accumAlphaSize;
113     }
114     if (data.sampleBuffers > 0) {
115         glxAttrib [pos++] = GLX.GLX_SAMPLE_BUFFERS;
116         glxAttrib [pos++] = data.sampleBuffers;
117     }
118     if (data.samples > 0) {
119         glxAttrib [pos++] = GLX.GLX_SAMPLES;
120         glxAttrib [pos++] = data.samples;
121     }
122     glxAttrib [pos++] = 0;
123     OS.gtk_widget_realize (handle);
124     auto window = OS.GTK_WIDGET_WINDOW (handle);
125     auto xDisplay = OS.gdk_x11_drawable_get_xdisplay (window);
126     auto infoPtr = GLX.glXChooseVisual (xDisplay, OS.XDefaultScreen (xDisplay), glxAttrib.ptr);
127     if (infoPtr is null) {
128         dispose ();
129         SWT.error (SWT.ERROR_UNSUPPORTED_DEPTH);
130     }
131 
132     vinfo = *infoPtr;
133     //tango.stdc.string.memmove (vinfo, infoPtr, XVisualInfo.sizeof);
134     OS.XFree (infoPtr);
135     auto screen = OS.gdk_screen_get_default ();
136     auto gdkvisual = OS.gdk_x11_screen_lookup_visual (screen, vinfo.visualid);
137     //FIXME- share lists
138     //context = GLX.glXCreateContext (xDisplay, info, share is null ? 0 : share.context, true);
139     context = GLX.glXCreateContext (xDisplay, &vinfo, null, true);
140     if (context is null) 
141         SWT.error (SWT.ERROR_NO_HANDLES);
142     GdkWindowAttr attrs;
143     attrs.width = 1;
144     attrs.height = 1;
145     attrs.event_mask = OS.GDK_KEY_PRESS_MASK | OS.GDK_KEY_RELEASE_MASK |
146         OS.GDK_FOCUS_CHANGE_MASK | OS.GDK_POINTER_MOTION_MASK |
147         OS.GDK_BUTTON_PRESS_MASK | OS.GDK_BUTTON_RELEASE_MASK |
148         OS.GDK_ENTER_NOTIFY_MASK | OS.GDK_LEAVE_NOTIFY_MASK |
149         OS.GDK_EXPOSURE_MASK | OS.GDK_VISIBILITY_NOTIFY_MASK |
150         OS.GDK_POINTER_MOTION_HINT_MASK;
151     attrs.window_type = OS.GDK_WINDOW_CHILD;
152     attrs.visual = gdkvisual;
153     glWindow = OS.gdk_window_new (window, &attrs, OS.GDK_WA_VISUAL);
154     OS.gdk_window_set_user_data ( glWindow, cast(void*) handle);
155     if ((style & SWT.NO_BACKGROUND) !is 0) 
156         OS.gdk_window_set_back_pixmap (window, null, false);
157     xWindow = OS.gdk_x11_drawable_get_xid ( glWindow );
158     OS.gdk_window_show (glWindow);
159 
160     Listener listener = new class() Listener {
161         public void handleEvent (Event event) {
162             switch (event.type) {
163             case SWT.Paint:
164                 /**
165                 * Bug in MESA.  MESA does some nasty sort of polling to try
166                 * to ensure that their buffer sizes match the current X state.
167                 * This state can be updated using glViewport().
168                 * FIXME: There has to be a better way of doing this.
169                 */
170                 int[4] viewport;
171                 GLX.glGetIntegerv (GLX.GL_VIEWPORT, viewport);
172                 GLX.glViewport (viewport [0],viewport [1],viewport [2],viewport [3]);
173                 break;
174             case SWT.Resize:
175                 Rectangle clientArea = getClientArea();
176                 OS.gdk_window_move (glWindow, clientArea.x, clientArea.y);
177                 OS.gdk_window_resize (glWindow, clientArea.width, clientArea.height);
178                 break;
179             case SWT.Dispose:
180                 auto window = OS.GTK_WIDGET_WINDOW (handle);
181                 auto xDisplay = OS.gdk_x11_drawable_get_xdisplay (window);
182                 if (context !is null) {
183                     if (GLX.glXGetCurrentContext () is context) {
184                         GLX.glXMakeCurrent (xDisplay, 0, null);
185                     }
186                     GLX.glXDestroyContext (xDisplay, context);
187                     context = null;
188                 }
189                 if (glWindow !is null) {
190                     OS.gdk_window_destroy (glWindow);
191                     glWindow = null;
192                 }
193                 break;
194             default: break;
195             }
196         }
197     };
198     addListener (SWT.Resize, listener);
199     addListener (SWT.Paint, listener);
200     addListener (SWT.Dispose, listener);
201 }
202 
203 /**
204  * Returns a GLData object describing the created context.
205  *  
206  * @return GLData description of the OpenGL context attributes
207  * @exception SWTException <ul>
208  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
209  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
210  * </ul>
211  */
212 public GLData getGLData () {
213     checkWidget ();
214     auto window = OS.GTK_WIDGET_WINDOW (handle);
215     auto xDisplay = OS.gdk_x11_drawable_get_xdisplay (window);
216     GLData data = new GLData ();
217     int [1] value;
218     GLX.glXGetConfig (xDisplay, &vinfo, GLX.GLX_DOUBLEBUFFER, value);
219     data.doubleBuffer = value [0] !is 0;
220     GLX.glXGetConfig (xDisplay, &vinfo, GLX.GLX_STEREO, value);
221     data.stereo = value [0] !is 0;
222     GLX.glXGetConfig (xDisplay, &vinfo, GLX.GLX_RED_SIZE, value);
223     data.redSize = value [0];
224     GLX.glXGetConfig (xDisplay, &vinfo, GLX.GLX_GREEN_SIZE, value);
225     data.greenSize = value [0];
226     GLX.glXGetConfig (xDisplay, &vinfo, GLX.GLX_BLUE_SIZE, value);
227     data.blueSize = value [0];
228     GLX.glXGetConfig (xDisplay, &vinfo, GLX.GLX_ALPHA_SIZE, value);
229     data.alphaSize = value [0];
230     GLX.glXGetConfig (xDisplay, &vinfo, GLX.GLX_DEPTH_SIZE, value);
231     data.depthSize = value [0];
232     GLX.glXGetConfig (xDisplay, &vinfo, GLX.GLX_STENCIL_SIZE, value);
233     data.stencilSize = value [0];
234     GLX.glXGetConfig (xDisplay, &vinfo, GLX.GLX_ACCUM_RED_SIZE, value);
235     data.accumRedSize = value [0];
236     GLX.glXGetConfig (xDisplay, &vinfo, GLX.GLX_ACCUM_GREEN_SIZE, value);
237     data.accumGreenSize = value [0];
238     GLX.glXGetConfig (xDisplay, &vinfo, GLX.GLX_ACCUM_BLUE_SIZE, value);
239     data.accumBlueSize = value [0];
240     GLX.glXGetConfig (xDisplay, &vinfo, GLX.GLX_ACCUM_ALPHA_SIZE, value);
241     data.accumAlphaSize = value [0];
242     GLX.glXGetConfig (xDisplay, &vinfo, GLX.GLX_SAMPLE_BUFFERS, value);
243     data.sampleBuffers = value [0];
244     GLX.glXGetConfig (xDisplay, &vinfo, GLX.GLX_SAMPLES, value);
245     data.samples = value [0];
246     return data;
247 }
248 
249 /**
250  * Returns a bool indicating whether the receiver's OpenGL context
251  * is the current context.
252  *  
253  * @return true if the receiver holds the current OpenGL context,
254  * false otherwise
255  * @exception SWTException <ul>
256  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
257  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
258  * </ul>
259  */
260 public bool isCurrent () {
261     checkWidget ();
262     return (GLX.glXGetCurrentContext () is context);
263 }
264 
265 /**
266  * Sets the OpenGL context associated with this GLCanvas to be the
267  * current GL context.
268  * 
269  * @exception SWTException <ul>
270  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
271  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
272  * </ul>
273  */
274 public void setCurrent () {
275     checkWidget ();
276     if (GLX.glXGetCurrentContext () is context) return;
277     auto window = OS.GTK_WIDGET_WINDOW (handle);
278     auto xDisplay = OS.gdk_x11_drawable_get_xdisplay (window);
279     GLX.glXMakeCurrent (xDisplay, xWindow, context);
280 }
281 
282 /**
283  * Swaps the front and back color buffers.
284  * 
285  * @exception SWTException <ul>
286  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
287  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
288  * </ul>
289  */
290 public void swapBuffers () {
291     checkWidget ();
292     auto window = OS.GTK_WIDGET_WINDOW (handle);
293     auto xDisplay = OS.gdk_x11_drawable_get_xdisplay (window);
294     GLX.glXSwapBuffers (xDisplay, xWindow);
295 }
296 }