1/*
2 * Copyright 1994-1997 Mark Kilgard, All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * GPL licensing not permitted.
6 *
7 * Authors:
8 *      Mark Kilgard
9 */
10
11
12#include <stdlib.h>
13
14
15#if !defined(_WIN32) && !(defined(__BEOS__) || defined(__HAIKU__))
16#include <GL/glx.h>
17#endif
18
19#ifdef __sgi
20#include <dlfcn.h>
21#endif
22
23#include "glutint.h"
24
25/* Grumble.  The IRIX 6.3 and early IRIX 6.4 OpenGL headers
26   support the video resize extension, but failed to define
27   GLX_SGIX_video_resize. */
28#ifdef GLX_SYNC_FRAME_SGIX
29#define GLX_SGIX_video_resize 1
30#endif
31
32#if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_video_resize)
33static int canVideoResize = -1;
34static int videoResizeChannel;
35#else
36static int canVideoResize = 0;
37#endif
38static int videoResizeInUse = 0;
39static int dx = -1, dy = -1, dw = -1, dh = -1;
40
41/* XXX Note that IRIX 6.2, 6.3, and some 6.4 versions have a
42   bug where programs seg-fault when they attempt video
43   resizing from an indirect OpenGL context (either local or
44   over a network). */
45
46#if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_video_resize)
47
48static volatile int errorCaught;
49
50/* ARGSUSED */
51static
52catchXSGIvcErrors(Display * dpy, XErrorEvent * event)
53{
54  errorCaught = 1;
55  return 0;
56}
57#endif
58
59/* CENTRY */
60int APIENTRY
61glutVideoResizeGet(GLenum param)
62{
63#if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_video_resize)
64  if (canVideoResize < 0) {
65    canVideoResize = __glutIsSupportedByGLX("GLX_SGIX_video_resize");
66    if (canVideoResize) {
67#if __sgi
68      /* This is a hack because IRIX 6.2, 6.3, and some 6.4
69         versions were released with GLX_SGIX_video_resize
70         being advertised by the X server though the video
71         resize extension is not actually supported.  We try to
72         determine if the libGL.so we are using actually has a
73         video resize entrypoint before we try to use the
74         feature. */
75      void (*func) (void);
76      void *glxDso = dlopen("libGL.so", RTLD_LAZY);
77
78      func = (void (*)(void)) dlsym(glxDso, "glXQueryChannelDeltasSGIX");
79      if (!func) {
80        canVideoResize = 0;
81      } else
82#endif
83      {
84        char *channelString;
85        int (*handler) (Display *, XErrorEvent *);
86
87        channelString = getenv("GLUT_VIDEO_RESIZE_CHANNEL");
88        videoResizeChannel = channelString ? atoi(channelString) : 0;
89
90        /* Work around another annoying problem with SGI's
91           GLX_SGIX_video_resize implementation.  Early IRIX
92           6.4 OpenGL's advertise the extension and have the
93           video resize API, but an XSGIvc X protocol errors
94           result trying to use the API.  Set up an error
95           handler to intercept what would otherwise be a fatal
96           error.  If an error was recieved, do not report that
97           video resize is possible. */
98        handler = XSetErrorHandler(catchXSGIvcErrors);
99
100        errorCaught = 0;
101
102        glXQueryChannelDeltasSGIX(__glutDisplay, __glutScreen,
103          videoResizeChannel, &dx, &dy, &dw, &dh);
104
105        /* glXQueryChannelDeltasSGIX is an inherent X server
106           round-trip so we know we will have gotten either the
107           correct reply or and error by this time. */
108        XSetErrorHandler(handler);
109
110        /* Still yet another work around.  In IRIX 6.4 betas,
111           glXQueryChannelDeltasSGIX will return as if it
112           succeeded, but the values are filled with junk.
113           Watch to make sure the delta variables really make
114           sense. */
115        if (errorCaught ||
116          dx < 0 || dy < 0 || dw < 0 || dh < 0 ||
117          dx > 2048 || dy > 2048 || dw > 2048 || dh > 2048) {
118          canVideoResize = 0;
119        }
120      }
121    }
122  }
123#endif /* GLX_SGIX_video_resize */
124
125  switch (param) {
126  case GLUT_VIDEO_RESIZE_POSSIBLE:
127    return canVideoResize;
128  case GLUT_VIDEO_RESIZE_IN_USE:
129    return videoResizeInUse;
130  case GLUT_VIDEO_RESIZE_X_DELTA:
131    return dx;
132  case GLUT_VIDEO_RESIZE_Y_DELTA:
133    return dy;
134  case GLUT_VIDEO_RESIZE_WIDTH_DELTA:
135    return dw;
136  case GLUT_VIDEO_RESIZE_HEIGHT_DELTA:
137    return dh;
138  case GLUT_VIDEO_RESIZE_X:
139  case GLUT_VIDEO_RESIZE_Y:
140  case GLUT_VIDEO_RESIZE_WIDTH:
141  case GLUT_VIDEO_RESIZE_HEIGHT:
142#if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_video_resize)
143    if (videoResizeInUse) {
144      int x, y, width, height;
145
146      glXQueryChannelRectSGIX(__glutDisplay, __glutScreen,
147        videoResizeChannel, &x, &y, &width, &height);
148      switch (param) {
149      case GLUT_VIDEO_RESIZE_X:
150        return x;
151      case GLUT_VIDEO_RESIZE_Y:
152        return y;
153      case GLUT_VIDEO_RESIZE_WIDTH:
154        return width;
155      case GLUT_VIDEO_RESIZE_HEIGHT:
156        return height;
157      }
158    }
159#endif
160    return -1;
161  default:
162    __glutWarning("invalid glutVideoResizeGet parameter: %d", param);
163    return -1;
164  }
165}
166
167void APIENTRY
168glutSetupVideoResizing(void)
169{
170#if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_video_resize)
171  if (glutVideoResizeGet(GLUT_VIDEO_RESIZE_POSSIBLE)) {
172    glXBindChannelToWindowSGIX(__glutDisplay, __glutScreen,
173      videoResizeChannel, __glutCurrentWindow->win);
174    videoResizeInUse = 1;
175  } else
176#endif
177    __glutFatalError("glutEstablishVideoResizing: video resizing not possible.\n");
178}
179
180void APIENTRY
181glutStopVideoResizing(void)
182{
183#if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_video_resize)
184  if (glutVideoResizeGet(GLUT_VIDEO_RESIZE_POSSIBLE)) {
185    if (videoResizeInUse) {
186      glXBindChannelToWindowSGIX(__glutDisplay, __glutScreen,
187        videoResizeChannel, None);
188      videoResizeInUse = 0;
189    }
190  }
191#endif
192}
193
194/* ARGSUSED */
195void APIENTRY
196glutVideoResize(int x, int y, int width, int height)
197{
198#if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_video_resize)
199  if (videoResizeInUse) {
200#ifdef GLX_SYNC_SWAP_SGIX
201    /* glXChannelRectSyncSGIX introduced in a patch to IRIX
202       6.2; the original unpatched IRIX 6.2 behavior is always
203       GLX_SYNC_SWAP_SGIX. */
204    glXChannelRectSyncSGIX(__glutDisplay, __glutScreen,
205      videoResizeChannel, GLX_SYNC_SWAP_SGIX);
206#endif
207    glXChannelRectSGIX(__glutDisplay, __glutScreen,
208      videoResizeChannel, x, y, width, height);
209  }
210#endif
211}
212
213/* ARGSUSED */
214void APIENTRY
215glutVideoPan(int x, int y, int width, int height)
216{
217#if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_video_resize)
218  if (videoResizeInUse) {
219#ifdef GLX_SYNC_FRAME_SGIX
220    /* glXChannelRectSyncSGIX introduced in a patch to IRIX
221       6.2; the original unpatched IRIX 6.2 behavior is always
222       GLX_SYNC_SWAP_SGIX.  We just ignore that we cannot
223       accomplish GLX_SYNC_FRAME_SGIX on IRIX unpatched 6.2;
224       this means you'd need a glutSwapBuffers to actually
225       realize the video resize. */
226    glXChannelRectSyncSGIX(__glutDisplay, __glutScreen,
227      videoResizeChannel, GLX_SYNC_FRAME_SGIX);
228#endif
229    glXChannelRectSGIX(__glutDisplay, __glutScreen,
230      videoResizeChannel, x, y, width, height);
231  }
232#endif
233}
234
235/* ENDCENTRY */
236