diff options
Diffstat (limited to 'clients/gears.c')
-rw-r--r-- | clients/gears.c | 485 |
1 files changed, 485 insertions, 0 deletions
diff --git a/clients/gears.c b/clients/gears.c new file mode 100644 index 00000000..30f4e688 --- /dev/null +++ b/clients/gears.c @@ -0,0 +1,485 @@ +/* + * 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. + */ + +#include "config.h" + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <time.h> + +#include <GL/gl.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <linux/input.h> +#include <wayland-client.h> + +#include "window.h" + +struct gears { + struct window *window; + struct widget *widget; + + struct display *d; + + EGLDisplay display; + EGLDisplay config; + EGLContext context; + GLfloat angle; + + struct { + GLfloat rotx; + GLfloat roty; + } view; + + int button_down; + int last_x, last_y; + + GLint gear_list[3]; + int fullscreen; + int frames; + uint32_t last_fps; +}; + +struct gear_template { + GLfloat material[4]; + GLfloat inner_radius; + GLfloat outer_radius; + GLfloat width; + GLint teeth; + GLfloat tooth_depth; +}; + +static const struct gear_template gear_templates[] = { + { { 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); +} + +static void +make_gear(const struct gear_template *t) +{ + GLint i; + GLfloat r0, r1, r2; + GLfloat angle, da; + GLfloat u, v, len; + + glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, t->material); + + 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; + + glShadeModel(GL_FLAT); + + glNormal3f(0.0, 0.0, 1.0); + + /* draw front face */ + glBegin(GL_QUAD_STRIP); + 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); + } + } + glEnd(); + + /* draw front sides of teeth */ + glBegin(GL_QUADS); + 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); + } + glEnd(); + + glNormal3f(0.0, 0.0, -1.0); + + /* draw back face */ + glBegin(GL_QUAD_STRIP); + 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); + } + } + glEnd(); + + /* draw back sides of teeth */ + glBegin(GL_QUADS); + 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); + } + glEnd(); + + /* draw outward faces of teeth */ + glBegin(GL_QUAD_STRIP); + 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(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5); + 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); + 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); + glNormal3f(cos(angle), sin(angle), 0.0); + 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); + 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); + 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); + glNormal3f(cos(angle), sin(angle), 0.0); + } + + glVertex3f(r1 * cos(0), r1 * sin(0), t->width * 0.5); + glVertex3f(r1 * cos(0), r1 * sin(0), -t->width * 0.5); + + glEnd(); + + glShadeModel(GL_SMOOTH); + + /* draw inside radius cylinder */ + glBegin(GL_QUAD_STRIP); + for (i = 0; i <= t->teeth; i++) { + angle = i * 2.0 * M_PI / t->teeth; + glNormal3f(-cos(angle), -sin(angle), 0.0); + glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5); + glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5); + } + glEnd(); +} + +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; + } +} + +static void +frame_callback(void *data, struct wl_callback *callback, uint32_t time) +{ + struct gears *gears = data; + + update_fps(gears, time); + + 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 +}; + +static int +motion_handler(struct widget *widget, struct input *input, + uint32_t time, float x, float y, void *data) +{ + 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; + } + + return CURSOR_LEFT_PTR; +} + +static void +button_handler(struct widget *widget, struct input *input, + uint32_t time, uint32_t button, + enum wl_pointer_button_state state, void *data) +{ + struct gears *gears = data; + + if (button == BTN_LEFT) { + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + gears->button_down = 1; + input_get_position(input, + &gears->last_x, &gears->last_y); + } else { + gears->button_down = 0; + } + } +} + +static void +redraw_handler(struct widget *widget, void *data) +{ + struct rectangle window_allocation; + struct rectangle allocation; + struct wl_callback *callback; + struct gears *gears = data; + + widget_get_allocation(gears->widget, &allocation); + window_get_allocation(gears->window, &window_allocation); + + if (display_acquire_window_surface(gears->d, + gears->window, + gears->context) < 0) { + die("Unable to acquire window surface, " + "compiled without cairo-egl?\n"); + } + + glViewport(allocation.x, + window_allocation.height - allocation.height - allocation.y, + allocation.width, allocation.height); + glScissor(allocation.x, + window_allocation.height - allocation.height - allocation.y, + allocation.width, allocation.height); + + glEnable(GL_SCISSOR_TEST); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glPushMatrix(); + + glTranslatef(0.0, 0.0, -50); + + glRotatef(gears->view.rotx, 1.0, 0.0, 0.0); + glRotatef(gears->view.roty, 0.0, 1.0, 0.0); + + glPushMatrix(); + glTranslatef(-3.0, -2.0, 0.0); + glRotatef(gears->angle, 0.0, 0.0, 1.0); + glCallList(gears->gear_list[0]); + glPopMatrix(); + + glPushMatrix(); + glTranslatef(3.1, -2.0, 0.0); + glRotatef(-2.0 * gears->angle - 9.0, 0.0, 0.0, 1.0); + glCallList(gears->gear_list[1]); + glPopMatrix(); + + glPushMatrix(); + glTranslatef(-3.1, 4.2, 0.0); + glRotatef(-2.0 * gears->angle - 25.0, 0.0, 0.0, 1.0); + glCallList(gears->gear_list[2]); + glPopMatrix(); + + glPopMatrix(); + + glFlush(); + + display_release_window_surface(gears->d, gears->window); + + callback = wl_surface_frame(window_get_wl_surface(gears->window)); + wl_callback_add_listener(callback, &listener, gears); +} + +static void +resize_handler(struct widget *widget, + int32_t width, int32_t height, void *data) +{ + struct gears *gears = data; + int32_t size, big, small; + + /* Constrain child size to be square and at least 300x300 */ + if (width < height) { + small = width; + big = height; + } else { + small = height; + big = width; + } + + if (gears->fullscreen) + size = small; + else + size = big; + + widget_set_size(gears->widget, size, size); +} + +static void +keyboard_focus_handler(struct window *window, + struct input *device, void *data) +{ + window_schedule_redraw(window); +} + +static void +fullscreen_handler(struct window *window, void *data) +{ + struct gears *gears = data; + + gears->fullscreen ^= 1; + window_set_fullscreen(window, gears->fullscreen); +} + +static struct gears * +gears_create(struct display *display) +{ + const int width = 450, height = 500; + struct gears *gears; + struct timeval tv; + int i; + + gears = zalloc(sizeof *gears); + gears->d = display; + gears->window = window_create(display); + gears->widget = frame_create(gears->window, gears); + window_set_title(gears->window, "Wayland Gears"); + + gears->display = display_get_egl_display(gears->d); + if (gears->display == NULL) + die("failed to create egl display\n"); + + eglBindAPI(EGL_OPENGL_API); + + gears->config = display_get_argb_egl_config(gears->d); + + gears->context = eglCreateContext(gears->display, gears->config, + EGL_NO_CONTEXT, NULL); + if (gears->context == NULL) + die("failed to create context\n"); + + if (!eglMakeCurrent(gears->display, NULL, NULL, gears->context)) + die("failed to make context current\n"); + + 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(); + } + + gears->button_down = 0; + gears->last_x = 0; + gears->last_y = 0; + + gears->view.rotx = 20.0; + gears->view.roty = 30.0; + + gettimeofday(&tv, NULL); + gears->last_fps = tv.tv_sec * 1000 + tv.tv_usec / 1000; + printf("Warning: FPS count is limited by the wayland compositor or monitor refresh rate\n"); + + 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); + + window_set_user_data(gears->window, gears); + widget_set_resize_handler(gears->widget, resize_handler); + widget_set_redraw_handler(gears->widget, redraw_handler); + widget_set_button_handler(gears->widget, button_handler); + widget_set_motion_handler(gears->widget, motion_handler); + window_set_keyboard_focus_handler(gears->window, + keyboard_focus_handler); + window_set_fullscreen_handler(gears->window, fullscreen_handler); + + window_schedule_resize(gears->window, width, height); + + return gears; +} + +int main(int argc, char *argv[]) +{ + struct display *d; + + d = display_create(&argc, argv); + if (d == NULL) { + fprintf(stderr, "failed to create display: %m\n"); + return -1; + } + gears_create(d); + display_run(d); + + return 0; +} |