1857b0bc6Smrg/*
2857b0bc6Smrg * DRM based mode setting test program
3857b0bc6Smrg * Copyright (C) 2013 Red Hat
4857b0bc6Smrg * Author: Rob Clark <robdclark@gmail.com>
5857b0bc6Smrg *
6857b0bc6Smrg * Permission is hereby granted, free of charge, to any person obtaining a
7857b0bc6Smrg * copy of this software and associated documentation files (the "Software"),
8857b0bc6Smrg * to deal in the Software without restriction, including without limitation
9857b0bc6Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10857b0bc6Smrg * and/or sell copies of the Software, and to permit persons to whom the
11857b0bc6Smrg * Software is furnished to do so, subject to the following conditions:
12857b0bc6Smrg *
13857b0bc6Smrg * The above copyright notice and this permission notice shall be included in
14857b0bc6Smrg * all copies or substantial portions of the Software.
15857b0bc6Smrg *
16857b0bc6Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17857b0bc6Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18857b0bc6Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19857b0bc6Smrg * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20857b0bc6Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21857b0bc6Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22857b0bc6Smrg * IN THE SOFTWARE.
23857b0bc6Smrg */
24857b0bc6Smrg
25857b0bc6Smrg#include <assert.h>
26857b0bc6Smrg#include <errno.h>
27857b0bc6Smrg#include <stdio.h>
28857b0bc6Smrg#include <stdlib.h>
29857b0bc6Smrg#include <stdint.h>
30857b0bc6Smrg#include <string.h>
31857b0bc6Smrg#include <signal.h>
32857b0bc6Smrg#include <sys/time.h>
3308d7334dSsnj#include <pthread.h>
3408d7334dSsnj#include <unistd.h>
35857b0bc6Smrg
36857b0bc6Smrg#include "xf86drm.h"
37857b0bc6Smrg#include "xf86drmMode.h"
38857b0bc6Smrg
393f012e29Smrg#include "util/common.h"
403f012e29Smrg
41857b0bc6Smrg#include "buffers.h"
42857b0bc6Smrg#include "cursor.h"
43857b0bc6Smrg
44857b0bc6Smrgstruct cursor {
45857b0bc6Smrg	int fd;
46857b0bc6Smrg	uint32_t bo_handle;
47857b0bc6Smrg	uint32_t crtc_id;
48857b0bc6Smrg	uint32_t crtc_w, crtc_h;
49857b0bc6Smrg	uint32_t w, h;
50857b0bc6Smrg
51857b0bc6Smrg	/* current state */
52857b0bc6Smrg	uint32_t enabled, x, y;
53857b0bc6Smrg	int32_t dx, dy;
54857b0bc6Smrg};
55857b0bc6Smrg
56857b0bc6Smrg#define MAX_CURSORS 8
57857b0bc6Smrgstatic struct cursor cursors[MAX_CURSORS];
58857b0bc6Smrgstatic int ncursors;
59857b0bc6Smrg
6008d7334dSsnjstatic pthread_t cursor_thread;
6108d7334dSsnjstatic int cursor_running;
6208d7334dSsnj
63857b0bc6Smrg/*
64857b0bc6Smrg * Timer driven program loops through these steps to move/enable/disable
65857b0bc6Smrg * the cursor
66857b0bc6Smrg */
67857b0bc6Smrg
68857b0bc6Smrgstruct cursor_step {
693f012e29Smrg	void (*run)(struct cursor *cursor, const struct cursor_step *step);
70857b0bc6Smrg	uint32_t msec;
71857b0bc6Smrg	uint32_t repeat;
72857b0bc6Smrg	int arg;
73857b0bc6Smrg};
74857b0bc6Smrg
75857b0bc6Smrgstatic uint32_t indx, count;
76857b0bc6Smrg
773f012e29Smrgstatic void set_cursor(struct cursor *cursor, const struct cursor_step *step)
78857b0bc6Smrg{
79857b0bc6Smrg	int enabled = (step->arg ^ count) & 0x1;
80857b0bc6Smrg	uint32_t handle = 0;
81857b0bc6Smrg
82857b0bc6Smrg	if (enabled)
83857b0bc6Smrg		handle = cursor->bo_handle;
84857b0bc6Smrg
85857b0bc6Smrg	cursor->enabled = enabled;
86857b0bc6Smrg
87857b0bc6Smrg	drmModeSetCursor(cursor->fd, cursor->crtc_id, handle, cursor->w, cursor->h);
88857b0bc6Smrg}
89857b0bc6Smrg
903f012e29Smrgstatic void move_cursor(struct cursor *cursor, const struct cursor_step *step)
91857b0bc6Smrg{
92857b0bc6Smrg	int x = cursor->x;
93857b0bc6Smrg	int y = cursor->y;
94857b0bc6Smrg
95857b0bc6Smrg	if (!cursor->enabled)
96857b0bc6Smrg		drmModeSetCursor(cursor->fd, cursor->crtc_id,
97857b0bc6Smrg				cursor->bo_handle, cursor->w, cursor->h);
98857b0bc6Smrg
99857b0bc6Smrg	/* calculate new cursor position: */
100857b0bc6Smrg	x += cursor->dx * step->arg;
101857b0bc6Smrg	y += cursor->dy * step->arg;
102857b0bc6Smrg
103857b0bc6Smrg	if (x < 0) {
104857b0bc6Smrg		x = 0;
105857b0bc6Smrg		cursor->dx = 1;
106857b0bc6Smrg	} else if (x > (int)cursor->crtc_w) {
107857b0bc6Smrg		x = cursor->crtc_w - 1;
108857b0bc6Smrg		cursor->dx = -1;
109857b0bc6Smrg	}
110857b0bc6Smrg
111857b0bc6Smrg	if (y < 0) {
112857b0bc6Smrg		y = 0;
113857b0bc6Smrg		cursor->dy = 1;
114857b0bc6Smrg	} else if (y > (int)cursor->crtc_h) {
115857b0bc6Smrg		y = cursor->crtc_h - 1;
116857b0bc6Smrg		cursor->dy = -1;
117857b0bc6Smrg	}
118857b0bc6Smrg
119857b0bc6Smrg	cursor->x = x;
120857b0bc6Smrg	cursor->y = y;
121857b0bc6Smrg
122857b0bc6Smrg	drmModeMoveCursor(cursor->fd, cursor->crtc_id, x, y);
123857b0bc6Smrg}
124857b0bc6Smrg
1253f012e29Smrgstatic const struct cursor_step steps[] = {
126857b0bc6Smrg		{  set_cursor, 10,   0,  1 },  /* enable */
127857b0bc6Smrg		{ move_cursor,  1, 100,  1 },
128857b0bc6Smrg		{ move_cursor,  1,  10, 10 },
129857b0bc6Smrg		{  set_cursor,  1, 100,  0 },  /* disable/enable loop */
130857b0bc6Smrg		{ move_cursor,  1,  10, 10 },
131857b0bc6Smrg		{ move_cursor,  9, 100,  1 },
132857b0bc6Smrg		{ move_cursor, 11, 100,  5 },
133857b0bc6Smrg		{  set_cursor, 17,  10,  0 },  /* disable/enable loop */
134857b0bc6Smrg		{ move_cursor,  9, 100,  1 },
135857b0bc6Smrg		{  set_cursor, 13,  10,  0 },  /* disable/enable loop */
136857b0bc6Smrg		{ move_cursor,  9, 100,  1 },
137857b0bc6Smrg		{  set_cursor, 13,  10,  0 },  /* disable/enable loop */
138857b0bc6Smrg		{  set_cursor, 10,   0,  0 },  /* disable */
139857b0bc6Smrg};
140857b0bc6Smrg
14108d7334dSsnjstatic void *cursor_thread_func(void *data)
142857b0bc6Smrg{
14308d7334dSsnj	while (cursor_running) {
1443f012e29Smrg		const struct cursor_step *step = &steps[indx % ARRAY_SIZE(steps)];
14508d7334dSsnj		int i;
14608d7334dSsnj
14708d7334dSsnj		for (i = 0; i < ncursors; i++) {
14808d7334dSsnj			struct cursor *cursor = &cursors[i];
14908d7334dSsnj			step->run(cursor, step);
15008d7334dSsnj		}
15108d7334dSsnj
15208d7334dSsnj		/* iterate to next count/step: */
15308d7334dSsnj		if (count < step->repeat) {
15408d7334dSsnj			count++;
15508d7334dSsnj		} else {
15608d7334dSsnj			count = 0;
15708d7334dSsnj			indx++;
15808d7334dSsnj		}
15908d7334dSsnj
16008d7334dSsnj		usleep(1000 * step->msec);
161857b0bc6Smrg	}
162857b0bc6Smrg
16308d7334dSsnj	return NULL;
164857b0bc6Smrg}
165857b0bc6Smrg
166857b0bc6Smrgint cursor_init(int fd, uint32_t bo_handle, uint32_t crtc_id,
167857b0bc6Smrg		uint32_t crtc_w, uint32_t crtc_h, uint32_t w, uint32_t h)
168857b0bc6Smrg{
169857b0bc6Smrg	struct cursor *cursor = &cursors[ncursors];
170857b0bc6Smrg
171857b0bc6Smrg	assert(ncursors < MAX_CURSORS);
172857b0bc6Smrg
173857b0bc6Smrg	cursor->fd = fd;
174857b0bc6Smrg	cursor->bo_handle = bo_handle;
175857b0bc6Smrg	cursor->crtc_id = crtc_id;
176857b0bc6Smrg	cursor->crtc_w = crtc_w;
177857b0bc6Smrg	cursor->crtc_h = crtc_h;
178857b0bc6Smrg	cursor->w = w;
179857b0bc6Smrg	cursor->h = h;
180857b0bc6Smrg
181857b0bc6Smrg	cursor->enabled = 0;
182857b0bc6Smrg	cursor->x = w/2;
183857b0bc6Smrg	cursor->y = h/2;
184857b0bc6Smrg	cursor->dx = 1;
185857b0bc6Smrg	cursor->dy = 1;
186857b0bc6Smrg
187857b0bc6Smrg	ncursors++;
188857b0bc6Smrg
189857b0bc6Smrg	return 0;
190857b0bc6Smrg}
191857b0bc6Smrg
192857b0bc6Smrgint cursor_start(void)
193857b0bc6Smrg{
19408d7334dSsnj	cursor_running = 1;
19508d7334dSsnj	pthread_create(&cursor_thread, NULL, cursor_thread_func, NULL);
196857b0bc6Smrg	printf("starting cursor\n");
197857b0bc6Smrg	return 0;
198857b0bc6Smrg}
199857b0bc6Smrg
200857b0bc6Smrgint cursor_stop(void)
201857b0bc6Smrg{
20208d7334dSsnj	cursor_running = 0;
20308d7334dSsnj	pthread_join(cursor_thread, NULL);
204857b0bc6Smrg	printf("cursor stopped\n");
205857b0bc6Smrg	return 0;
206857b0bc6Smrg}
207