summaryrefslogtreecommitdiff
path: root/riscos/message.c
blob: 20ad8f37fcb58882852a6d5d2a9fca4561704385 (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
/*
 * This file is part of NetSurf, http://netsurf.sourceforge.net/
 * Licensed under the GNU General Public License,
 *		  http://www.opensource.org/licenses/gpl-license
 * Copyright 2006 Richard Wilson <info@tinct.net>
 */

/** \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 "netsurf/riscos/message.h"
#include "netsurf/utils/log.h"
#include "netsurf/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;
}


/**
 * 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);
		}
	}
}