summaryrefslogtreecommitdiff
path: root/riscos/message.c
blob: 5a9bb15fa4726d73fd725df012bee053d48846b1 (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
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
/*
 * Copyright 2006 Richard Wilson <info@tinct.net>
 *
 * This file is part of NetSurf, http://www.netsurf-browser.org/
 *
 * NetSurf is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * NetSurf is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/** \file
 * Automated RISC OS message routing (implementation).
 */

#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include "oslib/os.h"
#include "oslib/wimp.h"
#include "riscos/message.h"
#include "utils/log.h"
#include "utils/utils.h"


struct active_message {
	unsigned int message_code;
	int id;
	void (*callback)(wimp_message *message);
	struct active_message *next;
	struct active_message *previous;
};
struct active_message *current_messages = NULL;

static struct active_message *ro_message_add(unsigned int message_code,
		void (*callback)(wimp_message *message));
static void ro_message_free(int ref);


/**
 * Sends a message and registers a return route for a bounce.
 *
 * \param event		the message event type
 * \param message	the message to register a route back for
 * \param task		the task to send a message to, or 0 for broadcast
 * \param callback	the code to call on a bounce
 * \return true on success, false otherwise
 */
bool ro_message_send_message(wimp_event_no event, wimp_message *message,
		wimp_t task, void (*callback)(wimp_message *message))
{
	os_error *error;

	assert(message);

	/* send a message */
	error = xwimp_send_message(event, message, task);
	if (error) {
		LOG(("xwimp_send_message: 0x%x: %s",
				error->errnum, error->errmess));
		warn_user("WimpError", error->errmess);
		return false;
	}

	/* register the default bounce handler */
	if (callback) {
		assert(event == wimp_USER_MESSAGE_RECORDED);
		return ro_message_register_handler(message, message->action,
				callback);
	}
	return true;
}


/**
 * Sends a message and registers a return route for a bounce.
 *
 * \param event		the message event type
 * \param message	the message to register a route back for
 * \param to_w		the window to send the message to
 * \param to_i		the icon
 * \param callback	the code to call on a bounce
 * \param to_t		receives the task handle of the window's creator
 * \return true on success, false otherwise
 */
bool ro_message_send_message_to_window(wimp_event_no event, wimp_message *message,
		wimp_w to_w, wimp_i to_i, void (*callback)(wimp_message *message),
		wimp_t *to_t)
{
	os_error *error;

	assert(message);

	/* send a message */
	error = xwimp_send_message_to_window(event, message, to_w, to_i, to_t);
	if (error) {
		LOG(("xwimp_send_message_to_window: 0x%x: %s",
				error->errnum, error->errmess));
		warn_user("WimpError", error->errmess);
		return false;
	}

	/* register the default bounce handler */
	if (callback) {
		assert(event == wimp_USER_MESSAGE_RECORDED);
		return ro_message_register_handler(message, message->action,
				callback);
	}
	return true;
}


/**
 * Registers a return route for a message.
 *
 * This function must be called after wimp_send_message so that a
 * valid value is present in the my_ref field.
 *
 * \param message	the message to register a route back for
 * \param message_code	the message action code to route
 * \param callback	the code to call for a matched action
 * \return true on success, false on memory exhaustion
 */
bool ro_message_register_handler(wimp_message *message,
		unsigned int message_code,
		void (*callback)(wimp_message *message))
{
	struct active_message *add;

	assert(message);
	assert(callback);

	add = ro_message_add(message_code, callback);
	if (add)
		add->id = message->my_ref;
	return (add != NULL);
}


/**
 * Registers a route for a message code.
 *
 * \param message_code	the message action code to route
 * \param callback	the code to call for a matched action
 * \return true on success, false on memory exhaustion
 */
bool ro_message_register_route(unsigned int message_code,
		void (*callback)(wimp_message *message))
{
	assert(callback);

	return (ro_message_add(message_code, callback) != NULL);
}

struct active_message *ro_message_add(unsigned int message_code,
		void (*callback)(wimp_message *message))
{
	struct active_message *add;

	assert(callback);

	add = (struct active_message *)malloc(sizeof(*add));
	if (!add)
		return NULL;
	add->message_code = message_code;
	add->id = 0;
	add->callback = callback;
	add->next = current_messages;
	add->previous = NULL;
	current_messages = add;
	return add;
}


/**
 * Attempts to route a message.
 *
 * \param message	the message to attempt to route
 * \return true if message was routed, false otherwise
 */
bool ro_message_handle_message(wimp_event_no event, wimp_message *message)
{
	struct active_message *test;
	bool handled = false;
	int ref;

	assert(message);

	if (event == wimp_USER_MESSAGE_ACKNOWLEDGE) {
		/* handle message acknowledgement */
		ref = message->my_ref;
		if (ref == 0)
			return false;

		/* handle the message */
		for (test = current_messages; test; test = test->next) {
			if ((ref == test->id) &&
					(message->action == test->message_code)) {
				handled = true;
				if (test->callback)
					test->callback(message);
				break;
			}
		}

		/* remove all handlers for this id */
		ro_message_free(ref);
		return handled;
	} else {
		/* handle simple routing */
		for (test = current_messages; test; test = test->next) {
			if ((test->id == 0) &&
					(message->action == test->message_code)) {
				test->callback(message);
				return true;
			}
		}
	}
	return false;
}


void ro_message_free(int ref)
{
	struct active_message *test;
	struct active_message *next = current_messages;

	while ((test = next)) {
		next = test->next;
		if (ref == test->id) {
			if (test->previous)
				test->previous->next = test->next;
			if (test->next)
				test->next->previous = test->previous;
			if (current_messages == test)
				current_messages = test->next;
			free(test);
		}
	}
}