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