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