xref: /haiku/src/servers/app/CursorSet.cpp (revision 830f67ef991407f287dbc1238aa5f5906d90c991)
1 /*
2  * Copyright 2001-2010, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		DarkWyrm <bpmagic@columbus.rr.com>
7  */
8 
9 
10 #include "CursorSet.h"
11 #include "ServerCursor.h"
12 
13 #include <AppServerLink.h>
14 #include <AutoDeleter.h>
15 #include <PortLink.h>
16 #include <ServerProtocol.h>
17 
18 #include <OS.h>
19 #include <String.h>
20 #include <File.h>
21 
22 #include <new>
23 
24 
25 /*!
26 	\brief Constructor
27 	\name Name of the cursor set.
28 */
29 CursorSet::CursorSet(const char *name)
30 	: BMessage()
31 {
32 	AddString("name", name != NULL ? name : "Untitled");
33 }
34 
35 
36 /*!
37 	\brief Saves the data in the cursor set to a file
38 	\param path Path of the file to save to.
39 	\param saveflags BFile open file flags. B_READ_WRITE is implied.
40 	\return
41 	- \c B_OK: Everything went fine.
42 	- \c B_BAD_VALUE: path is NULL
43 	- \c other value: See BFile::SetTo and BMessage::Flatten return codes
44 */
45 status_t
46 CursorSet::Save(const char *path, int32 saveFlags)
47 {
48 	if (path == NULL)
49 		return B_BAD_VALUE;
50 
51 	BFile file;
52 	status_t status = file.SetTo(path, B_READ_WRITE | saveFlags);
53 	if (status != B_OK)
54 		return status;
55 
56 	return Flatten(&file);
57 }
58 
59 
60 /*!
61 	\brief Loads the data into the cursor set from a file
62 	\param path Path of the file to load from.
63 	\return
64 	- \c B_OK: Everything went fine.
65 	- \c B_BAD_VALUE: path is NULL
66 	- \c other value: See BFile::SetTo and BMessage::Flatten return codes
67 */
68 status_t
69 CursorSet::Load(const char *path)
70 {
71 	if (path == NULL)
72 		return B_BAD_VALUE;
73 
74 	BFile file;
75 	status_t status = file.SetTo(path, B_READ_ONLY);
76 	if (status != B_OK)
77 		return status;
78 
79 	return Unflatten(&file);
80 }
81 
82 
83 /*!
84 	\brief Adds the cursor to the set and replaces any existing entry for the given specifier
85 	\param which System cursor specifier defined in CursorSet.h
86 	\param cursor BBitmap to represent the new cursor. Size should be 48x48 or less.
87 	\param hotspot The recipient of the hotspot for the cursor
88 	\return
89 	- \c B_OK: Everything went fine.
90 	- \c B_BAD_VALUE: cursor is NULL
91 	- \c other value: See BMessage::AddMessage return codes.
92 */
93 status_t
94 CursorSet::AddCursor(BCursorID which, const BBitmap *cursor,
95 	const BPoint &hotspot)
96 {
97 	if (cursor == NULL)
98 		return B_BAD_VALUE;
99 
100 	// Remove the data if it exists already
101 	RemoveData(_CursorWhichToString(which));
102 
103 	// Actually add the data to our set
104 	BMessage message((int32)which);
105 
106 	message.AddString("class", "bitmap");
107 	message.AddRect("_frame", cursor->Bounds());
108 	message.AddInt32("_cspace", cursor->ColorSpace());
109 
110 	message.AddInt32("_bmflags", 0);
111 	message.AddInt32("_rowbytes", cursor->BytesPerRow());
112 	message.AddPoint("hotspot", hotspot);
113 	message.AddData("_data", B_RAW_TYPE, cursor->Bits(), cursor->BitsLength());
114 
115 	return AddMessage(_CursorWhichToString(which), &message);
116 }
117 
118 
119 /*!
120 	\brief Adds the cursor to the set and replaces any existing entry for the given specifier
121 	\param which System cursor specifier defined in CursorSet.h
122 	\param data R5 cursor data pointer
123 	\return B_BAD_VALUE if data is NULL, otherwise B_OK
124 
125 	When possible, it is better to use the BBitmap version of AddCursor because this
126 	function must convert the R5 cursor data into a BBitmap
127 */
128 status_t
129 CursorSet::AddCursor(BCursorID which, uint8 *data)
130 {
131 	// Convert cursor data to a bitmap because all cursors are internally stored
132 	// as bitmaps
133 	if (data == NULL)
134 		return B_BAD_VALUE;
135 
136 	ObjectDeleter<BBitmap> bitmap(_CursorDataToBitmap(data));
137 	BPoint hotspot(data[2], data[3]);
138 
139 	status_t result = AddCursor(which, bitmap.Get(), hotspot);
140 
141 	return result;
142 }
143 
144 
145 /*!
146 	\brief Removes the data associated with the specifier from the cursor set
147 	\param which System cursor specifier defined in CursorSet.h
148 */
149 void
150 CursorSet::RemoveCursor(BCursorID which)
151 {
152 	RemoveData(_CursorWhichToString(which));
153 }
154 
155 
156 /*!
157 	\brief Retrieves a cursor from the set.
158 	\param which System cursor specifier defined in CursorSet.h
159 	\param cursor Bitmap** to receive a newly-allocated BBitmap containing the appropriate data
160 	\param hotspot The recipient of the hotspot for the cursor
161 	\return
162 	- \c B_OK: Success
163 	- \c B_BAD_VALUE: a NULL parameter was passed
164 	- \c B_NAME_NOT_FOUND: The specified cursor does not exist in this set
165 	- \c B_ERROR: An internal error occurred
166 
167 	BBitmaps created by this function are the responsibility of the caller.
168 */
169 status_t
170 CursorSet::FindCursor(BCursorID which, BBitmap **_cursor, BPoint *_hotspot)
171 {
172 	if (_cursor == NULL || _hotspot == NULL)
173 		return B_BAD_VALUE;
174 
175 	BMessage message;
176 	if (FindMessage(_CursorWhichToString(which), &message) != B_OK)
177 		return B_NAME_NOT_FOUND;
178 
179 	const char *className;
180 	if (message.FindString("class", &className) != B_OK
181 		|| strcmp(className, "cursor") != 0) {
182 		return B_ERROR;
183 	}
184 
185 	BPoint hotspot;
186 	if (message.FindPoint("hotspot", &hotspot) != B_OK)
187 		return B_ERROR;
188 
189 	const void *buffer;
190 	int32 bufferLength;
191 	if (message.FindData("_data", B_RAW_TYPE, (const void **)&buffer,
192 			(ssize_t *)&bufferLength) != B_OK) {
193 		return B_ERROR;
194 	}
195 
196 	BBitmap *bitmap = new(std::nothrow) BBitmap(message.FindRect("_frame"),
197 		(color_space)message.FindInt32("_cspace"), true);
198 	if (bitmap == NULL)
199 		return B_NO_MEMORY;
200 
201 	memcpy(bitmap->Bits(), buffer,
202 		min_c(bufferLength, bitmap->BitsLength()));
203 
204 	*_cursor = bitmap;
205 	*_hotspot = hotspot;
206 	return B_OK;
207 }
208 
209 
210 /*!
211 	\brief Retrieves a cursor from the set.
212 	\param which System cursor specifier defined in CursorSet.h
213 	\param cursor ServerCursor** to receive a newly-allocated ServerCursor containing the appropriate data
214 	\return
215 	- \c B_OK: Success
216 	- \c B_BAD_VALUE: a NULL parameter was passed
217 	- \c B_NAME_NOT_FOUND: The specified cursor does not exist in this set
218 	- \c B_ERROR: An internal error occurred
219 
220 	BBitmaps created by this function are the responsibility of the caller.
221 */
222 status_t
223 CursorSet::FindCursor(BCursorID which, ServerCursor **_cursor) const
224 {
225 	if (_cursor == NULL)
226 		return B_BAD_VALUE;
227 
228 	BMessage message;
229 	if (FindMessage(_CursorWhichToString(which), &message) != B_OK)
230 		return B_NAME_NOT_FOUND;
231 
232 	const char *className;
233 	if (message.FindString("class", &className) != B_OK
234 		|| strcmp(className, "cursor") != 0) {
235 		return B_ERROR;
236 	}
237 
238 	BPoint hotspot;
239 	if (message.FindPoint("hotspot", &hotspot) != B_OK)
240 		return B_ERROR;
241 
242 	const void *buffer;
243 	int32 bufferLength;
244 	if (message.FindData("_data", B_RAW_TYPE, (const void **)&buffer,
245 			(ssize_t *)&bufferLength) != B_OK) {
246 		return B_ERROR;
247 	}
248 
249 	ServerCursor *cursor = new(std::nothrow) ServerCursor(
250 		message.FindRect("_frame"), (color_space)message.FindInt32("_cspace"),
251 		0, hotspot);
252 	if (cursor == NULL)
253 		return B_NO_MEMORY;
254 
255 	memcpy(cursor->Bits(), buffer,
256 		min_c(bufferLength, (ssize_t)cursor->BitsLength()));
257 
258 	*_cursor = cursor;
259 	return B_OK;
260 }
261 
262 
263 /*!
264 	\brief Returns the name of the set
265 	\return The name of the set
266 */
267 const char *
268 CursorSet::GetName()
269 {
270 	const char *name;
271 	if (FindString("name", &name) == B_OK)
272 		return name;
273 
274 	return NULL;
275 }
276 
277 
278 /*!
279 	\brief Renames the cursor set
280 	\param name new name of the set.
281 
282 	This function will fail if given a NULL name
283 */
284 void
285 CursorSet::SetName(const char *name)
286 {
287 	if (name == NULL)
288 		return;
289 
290 	RemoveData("name");
291 	AddString("name", name);
292 }
293 
294 
295 /*!
296 	\brief Returns a string for the specified cursor attribute
297 	\param which System cursor specifier defined in CursorSet.h
298 	\return Name for the cursor specifier
299 */
300 const char *
301 CursorSet::_CursorWhichToString(BCursorID which) const
302 {
303 	switch (which) {
304 		case B_CURSOR_ID_SYSTEM_DEFAULT:
305 			return "System default";
306 		case B_CURSOR_ID_CONTEXT_MENU:
307 			return "Context menu";
308 		case B_CURSOR_ID_COPY:
309 			return "Copy";
310 		case B_CURSOR_ID_CROSS_HAIR:
311 			return "Cross hair";
312 		case B_CURSOR_ID_NO_CURSOR:
313 			return "No cursor";
314 		case B_CURSOR_ID_FOLLOW_LINK:
315 			return "Follow link";
316 		case B_CURSOR_ID_GRAB:
317 			return "Grab";
318 		case B_CURSOR_ID_GRABBING:
319 			return "Grabbing";
320 		case B_CURSOR_ID_HELP:
321 			return "Help";
322 		case B_CURSOR_ID_I_BEAM:
323 			return "I-beam";
324 		case B_CURSOR_ID_I_BEAM_HORIZONTAL:
325 			return "I-beam horizontal";
326 		case B_CURSOR_ID_MOVE:
327 			return "Move";
328 		case B_CURSOR_ID_NOT_ALLOWED:
329 			return "Not allowed";
330 		case B_CURSOR_ID_PROGRESS:
331 			return "Progress";
332 		case B_CURSOR_ID_RESIZE_NORTH:
333 			return "Resize North";
334 		case B_CURSOR_ID_RESIZE_EAST:
335 			return "Resize East";
336 		case B_CURSOR_ID_RESIZE_SOUTH:
337 			return "Resize South";
338 		case B_CURSOR_ID_RESIZE_WEST:
339 			return "Resize West";
340 		case B_CURSOR_ID_RESIZE_NORTH_EAST:
341 			return "Resize North East";
342 		case B_CURSOR_ID_RESIZE_NORTH_WEST:
343 			return "Resize North West";
344 		case B_CURSOR_ID_RESIZE_SOUTH_EAST:
345 			return "Resize South East";
346 		case B_CURSOR_ID_RESIZE_SOUTH_WEST:
347 			return "Resize South West";
348 		case B_CURSOR_ID_RESIZE_NORTH_SOUTH:
349 			return "Resize North South";
350 		case B_CURSOR_ID_RESIZE_EAST_WEST:
351 			return "Resize East West";
352 		case B_CURSOR_ID_RESIZE_NORTH_EAST_SOUTH_WEST:
353 			return "Resize North East South West";
354 		case B_CURSOR_ID_RESIZE_NORTH_WEST_SOUTH_EAST:
355 			return "Resize North West South East";
356 		case B_CURSOR_ID_ZOOM_IN:
357 			return "Zoom in";
358 		case B_CURSOR_ID_ZOOM_OUT:
359 			return "Zoom out";
360 		default:
361 			return "Invalid";
362 	}
363 }
364 
365 /*!
366 	\brief Creates a new BBitmap from R5 cursor data
367 	\param data Pointer to data in the R5 cursor data format
368 	\return NULL if data was NULL, otherwise a new BBitmap
369 
370 	BBitmaps returned by this function are always in the RGBA32 color space
371 */
372 BBitmap *
373 CursorSet::_CursorDataToBitmap(uint8 *data)
374 {
375 	// 68-byte array used in R5 for holding cursors.
376 	// This API has serious problems and should be deprecated (but supported)
377 	// in R2
378 
379 	if (data == NULL)
380 		return NULL;
381 
382 	// Now that we have all the setup, we're going to map (for now) the cursor
383 	// to RGBA32. Eventually, there will be support for 16 and 8-bit depths
384 	BBitmap *bitmap
385 		= new(std::nothrow) BBitmap(BRect(0,0,15,15), B_RGBA32, 0);
386 	if (bitmap == NULL)
387 		return NULL;
388 
389 	const uint32 black = 0xff000000;
390 	const uint32 white = 0xffffffff;
391 
392 	uint8 *buffer = (uint8 *)bitmap->Bits();
393 	uint16 *cursorPosition = (uint16 *)(data + 4);
394 	uint16 *maskPosition = (uint16 *)(data + 36);
395 
396 	// for each row in the cursor data
397 	for (uint8 y = 0; y < 16; y++) {
398 		uint32 *bitmapPosition
399 			= (uint32 *)(buffer + y * bitmap->BytesPerRow());
400 
401 		// TODO: use proper byteswap macros
402 		// On intel, our bytes end up swapped, so we must swap them back
403 		uint16 cursorFlip = (cursorPosition[y] & 0xff) << 8;
404 		cursorFlip |= (cursorPosition[y] & 0xff00) >> 8;
405 
406 		uint16 maskFlip = (maskPosition[y] & 0xff) << 8;
407 		maskFlip |= (maskPosition[y] & 0xff00) >> 8;
408 
409 		// for each column in each row of cursor data
410 		for (uint8 x = 0; x < 16; x++) {
411 			// Get the values and dump them to the bitmap
412 			uint16 bit = 1 << (15 - x);
413 			bitmapPosition[x] = ((cursorFlip & bit) != 0 ? black : white)
414 				& ((maskFlip & bit) != 0 ? 0xffffffff : 0x00ffffff);
415 		}
416 	}
417 
418 	return bitmap;
419 }
420