gears.c 12.5 KB
Newer Older
1
/*
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 * Copyright © 2008 Kristian Høgsberg
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting documentation, and
 * that the name of the copyright holders not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  The copyright holders make no representations
 * about the suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.
 *
 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 * OF THIS SOFTWARE.
21 22
 */

23 24
#include "config.h"

25 26
#include <stdint.h>
#include <stdio.h>
27
#include <stdlib.h>
28
#include <string.h>
29
#include <math.h>
30 31
#include <time.h>

32
#include <GL/gl.h>
33 34
#include <EGL/egl.h>
#include <EGL/eglext.h>
35

36
#include <linux/input.h>
37
#include <wayland-client.h>
38 39 40

#include "window.h"

41
struct gears {
42
	struct window *window;
43
	struct widget *widget;
44

45
	struct display *d;
46 47

	EGLDisplay display;
48
	EGLDisplay config;
49 50 51
	EGLContext context;
	GLfloat angle;

52 53 54 55 56 57 58 59
	struct {
		GLfloat rotx;
		GLfloat roty;
	} view;

	int button_down;
	int last_x, last_y;

60
	GLint gear_list[3];
Olivier Blin's avatar
Olivier Blin committed
61
	int fullscreen;
62 63
	int frames;
	uint32_t last_fps;
64 65
};

66 67 68 69 70 71 72 73 74
struct gear_template {
	GLfloat material[4];
	GLfloat inner_radius;
	GLfloat outer_radius;
	GLfloat width;
	GLint teeth;
	GLfloat tooth_depth;
};

75
static const struct gear_template gear_templates[] = {
76 77 78 79 80 81 82 83 84 85 86 87 88
	{ { 0.8, 0.1, 0.0, 1.0 }, 1.0, 4.0, 1.0, 20, 0.7 },
	{ { 0.0, 0.8, 0.2, 1.0 }, 0.5, 2.0, 2.0, 10, 0.7 },
	{ { 0.2, 0.2, 1.0, 1.0 }, 1.3, 2.0, 0.5, 10, 0.7 }, 
};

static GLfloat light_pos[4] = {5.0, 5.0, 10.0, 0.0};

static void die(const char *msg)
{
	fprintf(stderr, "%s", msg);
	exit(EXIT_FAILURE);
}

89
static void
90
make_gear(const struct gear_template *t)
91 92 93 94 95 96
{
	GLint i;
	GLfloat r0, r1, r2;
	GLfloat angle, da;
	GLfloat u, v, len;

97
	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, t->material);
98

99 100 101 102 103
	r0 = t->inner_radius;
	r1 = t->outer_radius - t->tooth_depth / 2.0;
	r2 = t->outer_radius + t->tooth_depth / 2.0;

	da = 2.0 * M_PI / t->teeth / 4.0;
104 105 106 107 108 109 110

	glShadeModel(GL_FLAT);

	glNormal3f(0.0, 0.0, 1.0);

	/* draw front face */
	glBegin(GL_QUAD_STRIP);
111 112 113 114 115 116 117
	for (i = 0; i <= t->teeth; i++) {
		angle = i * 2.0 * M_PI / t->teeth;
		glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
		glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
		if (i < t->teeth) {
			glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
			glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
118 119 120 121 122 123
		}
	}
	glEnd();

	/* draw front sides of teeth */
	glBegin(GL_QUADS);
124 125 126 127 128 129 130 131
	da = 2.0 * M_PI / t->teeth / 4.0;
	for (i = 0; i < t->teeth; i++) {
		angle = i * 2.0 * M_PI / t->teeth;

		glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
		glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), t->width * 0.5);
		glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), t->width * 0.5);
		glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
132 133 134 135 136 137 138
	}
	glEnd();

	glNormal3f(0.0, 0.0, -1.0);

	/* draw back face */
	glBegin(GL_QUAD_STRIP);
139 140 141 142 143 144 145
	for (i = 0; i <= t->teeth; i++) {
		angle = i * 2.0 * M_PI / t->teeth;
		glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
		glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
		if (i < t->teeth) {
			glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
			glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
146 147 148 149 150 151
		}
	}
	glEnd();

	/* draw back sides of teeth */
	glBegin(GL_QUADS);
152 153 154 155 156 157 158 159
	da = 2.0 * M_PI / t->teeth / 4.0;
	for (i = 0; i < t->teeth; i++) {
		angle = i * 2.0 * M_PI / t->teeth;

		glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
		glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -t->width * 0.5);
		glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -t->width * 0.5);
		glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
160 161 162 163 164
	}
	glEnd();

	/* draw outward faces of teeth */
	glBegin(GL_QUAD_STRIP);
165 166
	for (i = 0; i < t->teeth; i++) {
		angle = i * 2.0 * M_PI / t->teeth;
167

168 169
		glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
		glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
170 171 172 173 174 175
		u = r2 * cos(angle + da) - r1 * cos(angle);
		v = r2 * sin(angle + da) - r1 * sin(angle);
		len = sqrt(u * u + v * v);
		u /= len;
		v /= len;
		glNormal3f(v, -u, 0.0);
176 177
		glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), t->width * 0.5);
		glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -t->width * 0.5);
178
		glNormal3f(cos(angle), sin(angle), 0.0);
179 180
		glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), t->width * 0.5);
		glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -t->width * 0.5);
181 182 183
		u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da);
		v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da);
		glNormal3f(v, -u, 0.0);
184 185
		glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
		glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
186 187 188
		glNormal3f(cos(angle), sin(angle), 0.0);
	}

189 190
	glVertex3f(r1 * cos(0), r1 * sin(0), t->width * 0.5);
	glVertex3f(r1 * cos(0), r1 * sin(0), -t->width * 0.5);
191 192 193 194 195 196 197

	glEnd();

	glShadeModel(GL_SMOOTH);

	/* draw inside radius cylinder */
	glBegin(GL_QUAD_STRIP);
198 199
	for (i = 0; i <= t->teeth; i++) {
		angle = i * 2.0 * M_PI / t->teeth;
200
		glNormal3f(-cos(angle), -sin(angle), 0.0);
201 202
		glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
		glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
203 204 205 206
	}
	glEnd();
}

207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
static void
update_fps(struct gears *gears, uint32_t time)
{
	long diff_ms;

	gears->frames++;

	diff_ms = time - gears->last_fps;

	if (diff_ms > 5000) {
		float seconds = diff_ms / 1000.0;
		float fps = gears->frames / seconds;

		printf("%d frames in %6.3f seconds = %6.3f FPS\n", gears->frames, seconds, fps);
		fflush(stdout);

		gears->frames = 0;
		gears->last_fps = time;
	}
}

228
static void
229 230 231 232
frame_callback(void *data, struct wl_callback *callback, uint32_t time)
{
	struct gears *gears = data;

233 234
	update_fps(gears, time);

235 236 237 238 239 240 241 242 243 244 245 246
	gears->angle = (GLfloat) (time % 8192) * 360 / 8192.0;

	window_schedule_redraw(gears->window);

	if (callback)
		wl_callback_destroy(callback);
}

static const struct wl_callback_listener listener = {
	frame_callback
};

247 248
static int
motion_handler(struct widget *widget, struct input *input,
249
		uint32_t time, float x, float y, void *data)
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
{
	struct gears *gears = data;
	int offset_x, offset_y;
	float step = 0.5;

	if (gears->button_down) {
		offset_x = x - gears->last_x;
		offset_y = y - gears->last_y;
		gears->last_x = x;
		gears->last_y = y;
		gears->view.roty += offset_x * step;
		gears->view.rotx += offset_y * step;
		if (gears->view.roty >= 360)
			gears->view.roty = gears->view.roty - 360;
		if (gears->view.roty <= 0)
			gears->view.roty = gears->view.roty + 360;
		if (gears->view.rotx >= 360)
			gears->view.rotx = gears->view.rotx - 360;
		if (gears->view.rotx <= 0)
			gears->view.rotx = gears->view.rotx + 360;
	}

272
	return CURSOR_LEFT_PTR;
273 274 275 276
}

static void
button_handler(struct widget *widget, struct input *input,
277 278
		uint32_t time, uint32_t button,
		enum wl_pointer_button_state state, void *data)
279 280 281 282
{
	struct gears *gears = data;

	if (button == BTN_LEFT) {
283
		if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
284 285 286 287 288 289 290 291 292
			gears->button_down = 1;
			input_get_position(input,
					&gears->last_x, &gears->last_y);
		} else {
			gears->button_down = 0;
		}
	}
}

293
static void
294
redraw_handler(struct widget *widget, void *data)
295
{
296
	struct rectangle window_allocation;
297
	struct rectangle allocation;
298 299
	struct wl_callback *callback;
	struct gears *gears = data;
300

301
	widget_get_allocation(gears->widget, &allocation);
302 303
	window_get_allocation(gears->window, &window_allocation);

304 305 306 307 308 309
	if (display_acquire_window_surface(gears->d,
					    gears->window,
					    gears->context) < 0) {
		die("Unable to acquire window surface, "
		    "compiled without cairo-egl?\n");
	}
310 311
	
	glViewport(allocation.x,
312
		   window_allocation.height - allocation.height - allocation.y,
313
		   allocation.width, allocation.height);
314 315 316
	glScissor(allocation.x,
		  window_allocation.height - allocation.height - allocation.y,
		  allocation.width, allocation.height);
317 318

	glEnable(GL_SCISSOR_TEST);
319
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
320

321 322 323 324
	glPushMatrix();

	glTranslatef(0.0, 0.0, -50);

325 326
	glRotatef(gears->view.rotx, 1.0, 0.0, 0.0);
	glRotatef(gears->view.roty, 0.0, 1.0, 0.0);
327 328 329

	glPushMatrix();
	glTranslatef(-3.0, -2.0, 0.0);
330 331
	glRotatef(gears->angle, 0.0, 0.0, 1.0);
	glCallList(gears->gear_list[0]);
332 333 334 335
	glPopMatrix();

	glPushMatrix();
	glTranslatef(3.1, -2.0, 0.0);
336 337
	glRotatef(-2.0 * gears->angle - 9.0, 0.0, 0.0, 1.0);
	glCallList(gears->gear_list[1]);
338 339 340 341
	glPopMatrix();

	glPushMatrix();
	glTranslatef(-3.1, 4.2, 0.0);
342 343
	glRotatef(-2.0 * gears->angle - 25.0, 0.0, 0.0, 1.0);
	glCallList(gears->gear_list[2]);
344 345 346 347 348
	glPopMatrix();

	glPopMatrix();

	glFlush();
349

350
	display_release_window_surface(gears->d, gears->window);
351 352 353

	callback = wl_surface_frame(window_get_wl_surface(gears->window));
	wl_callback_add_listener(callback, &listener, gears);
354 355 356
}

static void
357
resize_handler(struct widget *widget,
358
	       int32_t width, int32_t height, void *data)
359 360
{
	struct gears *gears = data;
361
	int32_t size, big, small;
362

363
	/* Constrain child size to be square and at least 300x300 */
364 365 366 367 368 369
	if (width < height) {
		small = width;
		big = height;
	} else {
		small = height;
		big = width;
370 371
	}

372 373 374 375 376 377
	if (gears->fullscreen)
		size = small;
	else
		size = big;

	widget_set_size(gears->widget, size, size);
378 379
}

Kristian Høgsberg's avatar
Kristian Høgsberg committed
380 381
static void
keyboard_focus_handler(struct window *window,
382
		       struct input *device, void *data)
Kristian Høgsberg's avatar
Kristian Høgsberg committed
383
{
384
	window_schedule_redraw(window);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
385 386
}

Olivier Blin's avatar
Olivier Blin committed
387 388 389 390 391 392 393 394 395
static void
fullscreen_handler(struct window *window, void *data)
{
	struct gears *gears = data;

	gears->fullscreen ^= 1;
	window_set_fullscreen(window, gears->fullscreen);
}

396
static struct gears *
397
gears_create(struct display *display)
398
{
399
	const int width = 450, height = 500;
400
	struct gears *gears;
401
	struct timeval tv;
402 403
	int i;

Peter Hutterer's avatar
Peter Hutterer committed
404
	gears = zalloc(sizeof *gears);
405
	gears->d = display;
406
	gears->window = window_create(display);
407
	gears->widget = frame_create(gears->window, gears);
408
	window_set_title(gears->window, "Wayland Gears");
409

410
	gears->display = display_get_egl_display(gears->d);
411 412 413
	if (gears->display == NULL)
		die("failed to create egl display\n");

414 415
	eglBindAPI(EGL_OPENGL_API);

416
	gears->config = display_get_argb_egl_config(gears->d);
417 418 419

	gears->context = eglCreateContext(gears->display, gears->config,
					  EGL_NO_CONTEXT, NULL);
420 421 422
	if (gears->context == NULL)
		die("failed to create context\n");

423
	if (!eglMakeCurrent(gears->display, NULL, NULL, gears->context))
Tiago Vignatti's avatar
Tiago Vignatti committed
424
		die("failed to make context current\n");
425

426 427 428 429 430 431 432
	for (i = 0; i < 3; i++) {
		gears->gear_list[i] = glGenLists(1);
		glNewList(gears->gear_list[i], GL_COMPILE);
		make_gear(&gear_templates[i]);
		glEndList();
	}

433 434 435 436 437 438 439
	gears->button_down = 0;
	gears->last_x = 0;
	gears->last_y = 0;

	gears->view.rotx = 20.0;
	gears->view.roty = 30.0;

440 441
	gettimeofday(&tv, NULL);
	gears->last_fps = tv.tv_sec * 1000 + tv.tv_usec / 1000;
442
	printf("Warning: FPS count is limited by the wayland compositor or monitor refresh rate\n");
443

444 445 446 447 448 449 450 451 452 453 454 455 456 457
	glEnable(GL_NORMALIZE);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 200.0);
	glMatrixMode(GL_MODELVIEW);

	glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
	glEnable(GL_CULL_FACE);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_DEPTH_TEST);
	glClearColor(0, 0, 0, 0.92);

458
	window_set_user_data(gears->window, gears);
459 460
	widget_set_resize_handler(gears->widget, resize_handler);
	widget_set_redraw_handler(gears->widget, redraw_handler);
461 462
	widget_set_button_handler(gears->widget, button_handler);
	widget_set_motion_handler(gears->widget, motion_handler);
463 464
	window_set_keyboard_focus_handler(gears->window,
					  keyboard_focus_handler);
Olivier Blin's avatar
Olivier Blin committed
465
	window_set_fullscreen_handler(gears->window, fullscreen_handler);
466

467
	window_schedule_resize(gears->window, width, height);
468 469 470 471 472 473

	return gears;
}

int main(int argc, char *argv[])
{
474
	struct display *d;
475

476
	d = display_create(&argc, argv);
477 478 479 480
	if (d == NULL) {
		fprintf(stderr, "failed to create display: %m\n");
		return -1;
	}
481
	gears_create(d);
482
	display_run(d);
483 484 485

	return 0;
}