Codebase list lwjgl / debian/2.5+dfsg-3 src / native / linux / display.c
debian/2.5+dfsg-3

Tree @debian/2.5+dfsg-3 (Download .tar.gz)

display.c @debian/2.5+dfsg-3raw · history · blame

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
/* 
 * Copyright (c) 2002-2008 LWJGL Project
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are 
 * met:
 * 
 * * Redistributions of source code must retain the above copyright 
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'LWJGL' nor the names of 
 *   its contributors may be used to endorse or promote products derived 
 *   from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * $Id: display.c 2985 2008-04-07 18:42:36Z matzon $
 *
 * Linux specific library for display handling.
 *
 * @author elias_naur <elias_naur@users.sourceforge.net>
 * @version $Revision: 2985 $
 */

#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/extensions/xf86vmode.h>
#include <X11/extensions/Xrandr.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "common_tools.h"
#include "org_lwjgl_opengl_LinuxDisplay.h"

#define NUM_XRANDR_RETRIES 5

typedef struct {
	int width;
	int height;
	int freq;
	union {
		int size_index; // Data for Xrandr extension
		XF86VidModeModeInfo xf86vm_modeinfo; // Data for XF86VidMode extension
	} mode_data;
} mode_info;

static bool getXF86VidModeVersion(JNIEnv *env, Display *disp, int *major, int *minor) {
	int event_base, error_base;

	if (!XF86VidModeQueryExtension(disp, &event_base, &error_base)) {
		printfDebugJava(env, "XF86VidMode extension not available");
		return false;
	}
	if (!XF86VidModeQueryVersion(disp, major, minor)) {
		throwException(env, "Could not query XF86VidMode version");
		return false;
	}
	printfDebugJava(env, "XF86VidMode extension version %i.%i", *major, *minor);
	return true;
}

static bool getXrandrVersion(JNIEnv *env, Display *disp, int *major, int *minor) {
	int event_base, error_base;

	if (!XRRQueryExtension(disp, &event_base, &error_base)) {
		printfDebugJava(env, "Xrandr extension not available");
		return false;
	}
	if (!XRRQueryVersion(disp, major, minor)) {
		throwException(env, "Could not query Xrandr version");
		return false;
	}
	printfDebugJava(env, "Xrandr extension version %i.%i", *major, *minor);
	return true;
}

static bool isXrandrSupported(JNIEnv *env, Display *disp) {
	int major, minor;
	if (!getXrandrVersion(env, disp, &major, &minor))
		return false;
	return major >= 1;
}

static bool isXF86VidModeSupported(JNIEnv *env, Display *disp) {
	int minor_ver, major_ver;
	if (!getXF86VidModeVersion(env, disp, &major_ver, &minor_ver))
		return false;
	return major_ver >= 2;
}
	
JNIEXPORT jboolean JNICALL Java_org_lwjgl_opengl_LinuxDisplay_nIsXrandrSupported(JNIEnv *env, jclass unused, jlong display) {
	Display *disp = (Display *)(intptr_t)display;
	jboolean result = isXrandrSupported(env, disp) ? JNI_TRUE : JNI_FALSE;
	return result;
}

JNIEXPORT jboolean JNICALL Java_org_lwjgl_opengl_LinuxDisplay_nIsXF86VidModeSupported(JNIEnv *env, jclass unused, jlong display) {
	Display *disp = (Display *)(intptr_t)display;
	jboolean result = isXF86VidModeSupported(env, disp) ? JNI_TRUE : JNI_FALSE;
	return result;
}

static mode_info *getXrandrDisplayModes(Display *disp, int screen, int *num_modes) {
	int num_randr_sizes;
	XRRScreenSize *sizes = XRRSizes(disp, screen, &num_randr_sizes);
	mode_info *avail_modes = NULL;
	int list_size = 0;
	/* Count number of modes */
	int i;
	int mode_index = 0;
	for (i = 0; i < num_randr_sizes; i++) {
		int num_randr_rates;
		short *freqs = XRRRates(disp, screen, i, &num_randr_rates);
		int j;
		for (j = 0; j < num_randr_rates; j++) {
			if (list_size <= mode_index) {
				list_size += 1;
				avail_modes = (mode_info *)realloc(avail_modes, sizeof(mode_info)*list_size);
				if (avail_modes == NULL)
					return NULL;
			}
			avail_modes[mode_index].width = sizes[i].width;
			avail_modes[mode_index].height = sizes[i].height;
			avail_modes[mode_index].freq = freqs[j];
			avail_modes[mode_index].mode_data.size_index = i;
			mode_index++;
		}
	}
	*num_modes = mode_index;
	return avail_modes;
}

static mode_info *getXF86VidModeDisplayModes(Display *disp, int screen, int *num_modes) {
	int num_xf86vm_modes;
	XF86VidModeModeInfo **avail_xf86vm_modes;
	XF86VidModeGetAllModeLines(disp, screen, &num_xf86vm_modes, &avail_xf86vm_modes);
	mode_info *avail_modes = (mode_info *)malloc(sizeof(mode_info)*num_xf86vm_modes);
	if (avail_modes == NULL) {
		XFree(avail_xf86vm_modes);
		return NULL;
	}
	int i;
	for (i = 0; i < num_xf86vm_modes; i++) {
		avail_modes[i].width = avail_xf86vm_modes[i]->hdisplay;
		avail_modes[i].height = avail_xf86vm_modes[i]->vdisplay;
		avail_modes[i].freq = 0; // No frequency support in XF86VidMode
		avail_modes[i].mode_data.xf86vm_modeinfo = *avail_xf86vm_modes[i];
	}
	XFree(avail_xf86vm_modes);
	*num_modes = num_xf86vm_modes;
	return avail_modes;
}

static mode_info *getDisplayModes(Display *disp, int screen, jint extension, int *num_modes) {
	switch (extension) {
		case org_lwjgl_opengl_LinuxDisplay_XF86VIDMODE:
			return getXF86VidModeDisplayModes(disp, screen, num_modes);
		case org_lwjgl_opengl_LinuxDisplay_XRANDR:
			return getXrandrDisplayModes(disp, screen, num_modes);
		case org_lwjgl_opengl_LinuxDisplay_NONE:
			// fall through
		default:
			return NULL;
	}
}

static bool setXF86VidModeMode(Display *disp, int screen, mode_info *mode) {
	return True == XF86VidModeSwitchToMode(disp, screen, &mode->mode_data.xf86vm_modeinfo);
}

/* Try to set the mode specified through XRandR.
 * Return value is the Status code of the mode switch
 * The timestamp parameter is filled with the latest timestamp returned from XRRConfigTimes
 */
static Status trySetXrandrMode(Display *disp, int screen, mode_info *mode, Time *timestamp) {
	Status status;
	Drawable root_window = RootWindow(disp, screen);
	XRRScreenConfiguration *screen_configuration = XRRGetScreenInfo(disp, root_window);
	Time config_time;
	*timestamp = XRRConfigTimes(screen_configuration, &config_time);
	Rotation current_rotation;
	XRRConfigCurrentConfiguration(screen_configuration, &current_rotation);
	status = XRRSetScreenConfigAndRate(disp, screen_configuration, root_window, mode->mode_data.size_index, current_rotation, mode->freq, *timestamp);
	XRRFreeScreenConfigInfo(screen_configuration);
	return status;
}

static bool setXrandrMode(Display *disp, int screen, mode_info *mode) {
	int iteration;
	Time timestamp;
	Status status = trySetXrandrMode(disp, screen, mode, &timestamp);
	if (status == 0)
		return true; // Success
	Time new_timestamp;
	for (iteration = 0; iteration < NUM_XRANDR_RETRIES; iteration++) {
		status = trySetXrandrMode(disp, screen, mode, &new_timestamp);
		if (status == 0)
			return true; // Success
		if (new_timestamp == timestamp) {
			return false; // Failure, and the stamps are equal meaning that the failure is not merely transient
		}
		timestamp = new_timestamp;
	}
	return false;
}

static bool setMode(JNIEnv *env, Display *disp, int screen, jint extension, int width, int height, int freq) {
	int num_modes, i;
	mode_info *avail_modes = getDisplayModes(disp, screen, extension, &num_modes);
	if (avail_modes == NULL) {
		printfDebugJava(env, "Could not get display modes");
		return false;
	}
	bool result = false;
	for (i = 0; i < num_modes; ++i) {
		printfDebugJava(env, "Mode %d: %dx%d @%d", i, avail_modes[i].width, avail_modes[i].height, avail_modes[i].freq);
		if (avail_modes[i].width == width && avail_modes[i].height == height && avail_modes[i].freq == freq) {
			switch (extension) {
				case org_lwjgl_opengl_LinuxDisplay_XF86VIDMODE:
					if (!setXF86VidModeMode(disp, screen, &avail_modes[i])) {
						printfDebugJava(env, "Could not switch mode");
						continue;
					}
					break;
				case org_lwjgl_opengl_LinuxDisplay_XRANDR:
					if (!setXrandrMode(disp, screen, &avail_modes[i])) {
						printfDebugJava(env, "Could not switch mode");
						continue;
					}
					break;
				case org_lwjgl_opengl_LinuxDisplay_NONE: // Should never happen, since NONE imply no available display modes
				default:   // Should never happen
					continue;
			}
			result = true;
			break;
		}
	}
	free(avail_modes);
	XFlush(disp);
	return result;
}

static int getGammaRampLengthOfDisplay(JNIEnv *env, Display *disp, int screen) {
	int ramp_size;
	if (XF86VidModeGetGammaRampSize(disp, screen, &ramp_size) == False) {
		throwException(env, "XF86VidModeGetGammaRampSize call failed");
		return 0;
	}
	return ramp_size;
}

JNIEXPORT jobject JNICALL Java_org_lwjgl_opengl_LinuxDisplay_nConvertToNativeRamp(JNIEnv *env, jclass unused, jobject ramp_buffer, jint buffer_offset, jint length) {
	const jfloat *ramp_ptr = (const jfloat *)(*env)->GetDirectBufferAddress(env, ramp_buffer) + buffer_offset;
	jobject native_ramp = newJavaManagedByteBuffer(env, length*3*sizeof(unsigned short));
	if (native_ramp == NULL) {
		throwException(env, "Failed to allocate gamma ramp buffer");
		return NULL;
	}
	unsigned short *native_ramp_ptr = (unsigned short *)(*env)->GetDirectBufferAddress(env, native_ramp);
	int i;
	for (i = 0; i < length; i++) {
		float scaled_gamma = ramp_ptr[i]*0xffff;
		short scaled_gamma_short = (unsigned short)roundf(scaled_gamma);
		native_ramp_ptr[i] = scaled_gamma_short;
		native_ramp_ptr[i + length] = scaled_gamma_short;
		native_ramp_ptr[i + length*2] = scaled_gamma_short;
	}
	return native_ramp;
}

JNIEXPORT jobject JNICALL Java_org_lwjgl_opengl_LinuxDisplay_nGetCurrentGammaRamp(JNIEnv *env, jclass unused, jlong display, jint screen) {
	Display *disp = (Display *)(intptr_t)display;
	int ramp_size = getGammaRampLengthOfDisplay(env, disp, screen);
	jobject ramp_buffer = newJavaManagedByteBuffer(env, sizeof(unsigned short)*3*ramp_size);
	if (ramp_buffer == NULL) {
		throwException(env, "Could not allocate gamma ramp buffer");
		return NULL;
	}
	unsigned short *ramp = (unsigned short *)(*env)->GetDirectBufferAddress(env, ramp_buffer);
	if (!XF86VidModeGetGammaRamp(disp, screen, ramp_size, ramp,
				ramp + ramp_size, ramp + ramp_size*2)) {
		throwException(env, "Could not get the current gamma ramp");
		return NULL;
	}
	return ramp_buffer;
}

static void setGamma(JNIEnv *env, Display *disp, int screen, jobject ramp_buffer) {
	if (ramp_buffer == NULL)
		return;
	unsigned short *ramp_ptr = (unsigned short *)(*env)->GetDirectBufferAddress(env, ramp_buffer);
	jlong capacity = (*env)->GetDirectBufferCapacity(env, ramp_buffer);
	int size = capacity/(sizeof(unsigned short)*3);
	if (size == 0)
		return;
	if (XF86VidModeSetGammaRamp(disp, screen, size, ramp_ptr, ramp_ptr + size, ramp_ptr + size*2) == False) {
		throwException(env, "Could not set gamma ramp.");
	}
}

static bool switchDisplayMode(JNIEnv * env, Display *disp, int screen, jint extension, jobject mode) {
	if (mode == NULL) {
		throwException(env, "mode must be non-null");
		return false;
	}
	jclass cls_displayMode = (*env)->GetObjectClass(env, mode);
	jfieldID fid_width = (*env)->GetFieldID(env, cls_displayMode, "width", "I");
	jfieldID fid_height = (*env)->GetFieldID(env, cls_displayMode, "height", "I");
	jfieldID fid_freq = (*env)->GetFieldID(env, cls_displayMode, "freq", "I");
	int width = (*env)->GetIntField(env, mode, fid_width);
	int height = (*env)->GetIntField(env, mode, fid_height);
	int freq = (*env)->GetIntField(env, mode, fid_freq);
	if (!setMode(env, disp, screen, extension, width, height, freq)) {
		throwException(env, "Could not switch mode.");
		return false;
	}
	return true;
}

static jobjectArray getAvailableDisplayModes(JNIEnv * env, Display *disp, int screen, jint extension) {
	int num_modes, i;
	mode_info *avail_modes;
	int bpp = XDefaultDepth(disp, screen);
	avail_modes = getDisplayModes(disp, screen, extension, &num_modes);
	if (avail_modes == NULL) {
		printfDebugJava(env, "Could not get display modes");
		return NULL;
	}
	// Allocate an array of DisplayModes big enough
	jclass displayModeClass = (*env)->FindClass(env, "org/lwjgl/opengl/DisplayMode");
	jobjectArray ret = (*env)->NewObjectArray(env, num_modes, displayModeClass, NULL);
	jmethodID displayModeConstructor = (*env)->GetMethodID(env, displayModeClass, "<init>", "(IIII)V");

	for (i = 0; i < num_modes; i++) {
		jobject displayMode = (*env)->NewObject(env, displayModeClass, displayModeConstructor, avail_modes[i].width, avail_modes[i].height, bpp, avail_modes[i].freq);
		(*env)->SetObjectArrayElement(env, ret, i, displayMode);
	}
	free(avail_modes);
	return ret;
}

static jobject getCurrentXRandrMode(JNIEnv * env, Display *disp, int screen) {
	Drawable root_window = RootWindow(disp, screen);
	XRRScreenConfiguration *config = XRRGetScreenInfo(disp, root_window);
	if (config == NULL) {
		throwException(env, "Could not get current screen configuration.");
		return NULL;
	}
	short rate = XRRConfigCurrentRate(config);
	Rotation current_rotation;
	SizeID size_index = XRRConfigCurrentConfiguration(config, &current_rotation);
	int n_sizes;
	XRRScreenSize *sizes = XRRConfigSizes(config, &n_sizes);
	if (size_index >= n_sizes) {
		throwFormattedException(env, "Xrandr current index (%d) is larger than or equals to the number of sizes (%d).", size_index, n_sizes);
		XRRFreeScreenConfigInfo(config);
		return NULL;
	}
	XRRScreenSize current_size = sizes[size_index];
	XRRFreeScreenConfigInfo(config);
	int bpp = XDefaultDepth(disp, screen);
	jclass displayModeClass = (*env)->FindClass(env, "org/lwjgl/opengl/DisplayMode");
	jmethodID displayModeConstructor = (*env)->GetMethodID(env, displayModeClass, "<init>", "(IIII)V");
	jobject displayMode = (*env)->NewObject(env, displayModeClass, displayModeConstructor, current_size.width, current_size.height, bpp, rate);
	return displayMode;
}

JNIEXPORT jobject JNICALL Java_org_lwjgl_opengl_LinuxDisplay_nGetCurrentXRandrMode(JNIEnv *env, jclass unused, jlong display, jint screen) {
	Display *disp = (Display *)(intptr_t)display;
	return getCurrentXRandrMode(env, disp, screen);
}

JNIEXPORT jobjectArray JNICALL Java_org_lwjgl_opengl_LinuxDisplay_nGetAvailableDisplayModes(JNIEnv *env, jclass clazz, jlong display, jint screen, jint extension) {
	Display *disp = (Display *)(intptr_t)display;
	return getAvailableDisplayModes(env, disp, screen, extension);
}

JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_nSwitchDisplayMode(JNIEnv *env, jclass clazz, jlong display, jint screen, jint extension, jobject mode) {
	Display *disp = (Display *)(intptr_t)display;
	switchDisplayMode(env, disp, screen, extension, mode);
}

JNIEXPORT jint JNICALL Java_org_lwjgl_opengl_LinuxDisplay_nGetGammaRampLength(JNIEnv *env, jclass clazz, jlong display_ptr, jint screen) {
	Display *disp = (Display *)(intptr_t)display_ptr;
	return (jint)getGammaRampLengthOfDisplay(env, disp, screen);
}

JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_nSetGammaRamp(JNIEnv *env, jclass clazz, jlong display, jint screen, jobject gamma_buffer) {
	Display *disp = (Display *)(intptr_t)display;
	setGamma(env, disp, screen, gamma_buffer);
}