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 */
CursorSet(const char * name)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
Save(const char * path,int32 saveFlags)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
Load(const char * path)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
AddCursor(BCursorID which,const BBitmap * cursor,const BPoint & hotspot)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
AddCursor(BCursorID which,uint8 * data)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
RemoveCursor(BCursorID which)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
FindCursor(BCursorID which,BBitmap ** _cursor,BPoint * _hotspot)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
FindCursor(BCursorID which,ServerCursor ** _cursor) const223 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 *
GetName()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
SetName(const char * name)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 *
_CursorWhichToString(BCursorID which) const301 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 *
_CursorDataToBitmap(uint8 * data)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