summaryrefslogtreecommitdiffstats
path: root/arch/sandbox/os/sdl.c
blob: 13178abfc044f703cff655d9fe29e6c3c63b33ad (plain)
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
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2021 Ahmad Fatoum
 */

#include <stdio.h>
#include <stdbool.h>
#include <SDL.h>
#include <mach/linux.h>

static void sdl_perror(const char *what)
{
	printf("SDL: Could not %s: %s.\n", what, SDL_GetError());
}

static struct sdl_fb_info info;
static SDL_atomic_t shutdown;
SDL_Window *window;

static int scanout(void *ptr)
{
	SDL_Renderer *renderer;
	SDL_Surface *surface;
	SDL_Texture *texture;
	void *buf = info.screen_base;
	int ret = -1;

	renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
	if (!renderer) {
		sdl_perror("create renderer");
		return -1;
	}

	surface = SDL_CreateRGBSurface(0, info.xres, info.yres, info.bpp,
				       info.rmask, info.gmask, info.bmask, info.amask);
	if (!surface) {
		sdl_perror("create surface");
		goto destroy_renderer;
	}

	texture = SDL_CreateTextureFromSurface(renderer, surface);
	if (!texture) {
		sdl_perror("create texture");
		goto free_surface;
	}

	while (!SDL_AtomicGet(&shutdown)) {
		SDL_Delay(100);

		SDL_UpdateTexture(texture, NULL, buf, surface->pitch);
		SDL_RenderClear(renderer);
		SDL_RenderCopy(renderer, texture, NULL, NULL);
		SDL_RenderPresent(renderer);
	}

	ret = 0;

	SDL_DestroyTexture(texture);
free_surface:
	SDL_FreeSurface(surface);
destroy_renderer:
	SDL_DestroyRenderer(renderer);

	return ret;
}

static SDL_Thread *thread;

void sdl_video_close(void)
{
	SDL_AtomicSet(&shutdown, true); /* implies full memory barrier */
	SDL_WaitThread(thread, NULL);
	SDL_AtomicSet(&shutdown, false);
	SDL_DestroyWindow(window);
	SDL_QuitSubSystem(SDL_INIT_VIDEO);
}

int sdl_video_open(const struct sdl_fb_info *_info)
{
	info = *_info;

	if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) {
		sdl_perror("initialize SDL Video");
		return -1;
	}

	window = SDL_CreateWindow("barebox", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
				  info.xres, info.yres, 0);
	if (!window) {
		sdl_perror("create window");
		goto quit_subsystem;
	}

	/* All scanout needs to happen in the same thread, because not all
	 * graphic backends are thread-safe. The window is created in the main
	 * thread though to work around libEGL crashing with SDL_VIDEODRIVER=wayland
	 */

	thread = SDL_CreateThread(scanout, "video-scanout", NULL);
	if (!thread) {
		sdl_perror("start scanout thread");
		goto destroy_window;
	}

	return 0;

destroy_window:
	SDL_DestroyWindow(window);
quit_subsystem:
	SDL_QuitSubSystem(SDL_INIT_VIDEO);

	return -1;
}

static SDL_AudioDeviceID dev;

int sdl_sound_init(unsigned sample_rate)
{
	SDL_AudioSpec audiospec = {
		.freq = sample_rate,
		.format = AUDIO_S16,
		.channels = 1,
		.samples = 2048,
	};

	if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
		sdl_perror("initialize SDL Audio");
		return -1;
	}

	dev = SDL_OpenAudioDevice(NULL, 0, &audiospec, NULL, 0);
	if (!dev) {
		sdl_perror("initialize open audio device");
		SDL_QuitSubSystem(SDL_INIT_AUDIO);
		return -1;
	}

	SDL_PauseAudioDevice(dev, 0);
	return 0;
}

void sdl_sound_close(void)
{
	SDL_QuitSubSystem(SDL_INIT_AUDIO);
}

int sdl_sound_play(const void *data, unsigned nsamples)
{
	/* core sound support handles all the queueing for us */
	SDL_ClearQueuedAudio(dev);
	return SDL_QueueAudio(dev, data, nsamples * sizeof(uint16_t));
}

void sdl_sound_stop(void)
{
	SDL_ClearQueuedAudio(dev);
}