xref: /haiku/src/servers/app/CursorSet.cpp (revision 16d5c24e533eb14b7b8a99ee9f3ec9ba66335b1e)
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(cursor_which 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(cursor_which 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(cursor_which 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(cursor_which 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(cursor_which which, ServerCursor **_cursor)
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(cursor_which which)
285 {
286 	switch (which) {
287 		case B_CURSOR_DEFAULT:
288 			return "Default";
289 		case B_CURSOR_TEXT:
290 			return "Text";
291 		case B_CURSOR_MOVE:
292 			return "Move";
293 		case B_CURSOR_DRAG:
294 			return "Drag";
295 		case B_CURSOR_RESIZE:
296 			return "Resize";
297 		case B_CURSOR_RESIZE_NWSE:
298 			return "Resize NWSE";
299 		case B_CURSOR_RESIZE_NESW:
300 			return "Resize NESW";
301 		case B_CURSOR_RESIZE_NS:
302 			return "Resize NS";
303 		case B_CURSOR_RESIZE_EW:
304 			return "Resize EW";
305 		case B_CURSOR_OTHER:
306 			return "Other";
307 		default:
308 			return "Invalid";
309 	}
310 }
311 
312 /*!
313 	\brief Creates a new BBitmap from R5 cursor data
314 	\param data Pointer to data in the R5 cursor data format
315 	\return NULL if data was NULL, otherwise a new BBitmap
316 
317 	BBitmaps returned by this function are always in the RGBA32 color space
318 */
319 BBitmap *
320 CursorSet::_CursorDataToBitmap(uint8 *data)
321 {
322 	// 68-byte array used in R5 for holding cursors.
323 	// This API has serious problems and should be deprecated(but supported) in R2
324 
325 	// Now that we have all the setup, we're going to map (for now) the cursor
326 	// to RGBA32. Eventually, there will be support for 16 and 8-bit depths
327 	if (data) {
328 	 	BBitmap *bmp = new BBitmap(BRect(0,0,15,15),B_RGBA32,0);
329 		uint32 black = 0xFF000000, white=0xFFFFFFFF, *bmppos;
330 		uint16 *cursorpos, *maskpos, cursorflip, maskflip;
331 		uint16 cursorval, maskval, powval;
332 		uint8 i, j, *_buffer;
333 
334 		_buffer=(uint8*)bmp->Bits();
335 		cursorpos=(uint16*)(data+4);
336 		maskpos=(uint16*)(data+36);
337 
338 		// for each row in the cursor data
339 		for (j = 0; j < 16; j++) {
340 			bmppos = (uint32*)(_buffer+ (j*bmp->BytesPerRow()) );
341 
342 			// On intel, our bytes end up swapped, so we must swap them back
343 			cursorflip = (cursorpos[j] & 0xFF) << 8;
344 			cursorflip |= (cursorpos[j] & 0xFF00) >> 8;
345 
346 			maskflip = (maskpos[j] & 0xFF) << 8;
347 			maskflip |= (maskpos[j] & 0xFF00) >> 8;
348 
349 			// for each column in each row of cursor data
350 			for (i = 0; i < 16; i++) {
351 				// Get the values and dump them to the bitmap
352 				powval = 1 << (15-i);
353 				cursorval = cursorflip & powval;
354 				maskval = maskflip & powval;
355 				bmppos[i] = (cursorval != 0 ? black : white)
356 					& (maskval > 0 ? 0xFFFFFFFF : 0x00FFFFFF);
357 			}
358 		}
359 		return bmp;
360 	}
361 
362 	return NULL;
363 }
364