xref: /haiku/src/servers/app/CursorSet.cpp (revision fef6144999c2fa611f59ee6ffe6dd7999501385c)
1 //------------------------------------------------------------------------------
2 //	Copyright (c) 2001-2005, Haiku, Inc.
3 //
4 //	Permission is hereby granted, free of charge, to any person obtaining a
5 //	copy of this software and associated documentation files (the "Software"),
6 //	to deal in the Software without restriction, including without limitation
7 //	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 //	and/or sell copies of the Software, and to permit persons to whom the
9 //	Software is furnished to do so, subject to the following conditions:
10 //
11 //	The above copyright notice and this permission notice shall be included in
12 //	all copies or substantial portions of the Software.
13 //
14 //	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 //	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 //	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 //	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 //	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 //	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 //	DEALINGS IN THE SOFTWARE.
21 //
22 //	File Name:		CursorSet.cpp
23 //	Author:			DarkWyrm <bpmagic@columbus.rr.com>
24 //	Description:	Private file encapsulating OBOS system cursor API
25 //
26 //------------------------------------------------------------------------------
27 #include <PortLink.h>
28 #include <AppServerLink.h>
29 #include <ServerProtocol.h>
30 #include <OS.h>
31 #include <String.h>
32 #include <File.h>
33 #include "CursorSet.h"
34 #include "ServerCursor.h"
35 
36 
37 /*!
38 	\brief Constructor
39 	\name Name of the cursor set.
40 */
41 CursorSet::CursorSet(const char *name)
42 	: BMessage()
43 {
44 	AddString("name", name ? name : "Untitled");
45 }
46 
47 /*!
48 	\brief Saves the data in the cursor set to a file
49 	\param path Path of the file to save to.
50 	\param saveflags BFile open file flags. B_READ_WRITE is implied.
51 	\return
52 	- \c B_OK: Everything went fine.
53 	- \c B_BAD_VALUE: path is NULL
54 	- \c other value: See BFile::SetTo and BMessage::Flatten return codes
55 */
56 status_t
57 CursorSet::Save(const char *path, int32 saveflags)
58 {
59 	if (!path)
60 		return B_BAD_VALUE;
61 
62 	BFile file;
63 	status_t status = file.SetTo(path, B_READ_WRITE | saveflags);
64 	if (status != B_OK)
65 		return status;
66 
67 	return Flatten(&file);
68 }
69 
70 /*!
71 	\brief Loads the data into the cursor set from a file
72 	\param path Path of the file to load from.
73 	\return
74 	- \c B_OK: Everything went fine.
75 	- \c B_BAD_VALUE: path is NULL
76 	- \c other value: See BFile::SetTo and BMessage::Flatten return codes
77 */
78 status_t
79 CursorSet::Load(const char *path)
80 {
81 	if (!path)
82 		return B_BAD_VALUE;
83 
84 	BFile file;
85 	status_t status = file.SetTo(path, B_READ_ONLY);
86 	if (status != B_OK)
87 		return status;
88 
89 	return Unflatten(&file);
90 }
91 
92 /*!
93 	\brief Adds the cursor to the set and replaces any existing entry for the given specifier
94 	\param which System cursor specifier defined in CursorSet.h
95 	\param cursor BBitmap to represent the new cursor. Size should be 48x48 or less.
96 	\param hotspot The recipient of the hotspot for the cursor
97 	\return B_BAD_VALUE if cursor is NULL, otherwise B_OK
98 */
99 status_t
100 CursorSet::AddCursor(cursor_which which, const BBitmap *cursor, const BPoint &hotspot)
101 {
102 	if (!cursor)
103 		return B_BAD_VALUE;
104 
105 	// Remove the data if it exists already
106 	RemoveData(CursorWhichToString(which));
107 
108 	// Actually add the data to our set
109 	BMessage msg((int32)which);
110 
111 	msg.AddString("class","bitmap");
112 	msg.AddRect("_frame",cursor->Bounds());
113 	msg.AddInt32("_cspace",cursor->ColorSpace());
114 
115 	msg.AddInt32("_bmflags",0);
116 	msg.AddInt32("_rowbytes",cursor->BytesPerRow());
117 	msg.AddPoint("hotspot",hotspot);
118 	msg.AddData("_data",B_RAW_TYPE,cursor->Bits(), cursor->BitsLength());
119 	AddMessage(CursorWhichToString(which),&msg);
120 
121 	return B_OK;
122 }
123 
124 /*!
125 	\brief Adds the cursor to the set and replaces any existing entry for the given specifier
126 	\param which System cursor specifier defined in CursorSet.h
127 	\param data R5 cursor data pointer
128 	\return B_BAD_VALUE if data is NULL, otherwise B_OK
129 
130 	When possible, it is better to use the BBitmap version of AddCursor because this
131 	function must convert the R5 cursor data into a BBitmap
132 */
133 status_t
134 CursorSet::AddCursor(cursor_which which, int8 *data)
135 {
136 	// Convert cursor data to a bitmap because all cursors are internally stored
137 	// as bitmaps
138 	if (!data)
139 		return B_BAD_VALUE;
140 
141 	BBitmap *bmp = CursorDataToBitmap(data);
142 	BPoint pt(data[2],data[3]);
143 
144 	status_t stat = AddCursor(which,bmp,pt);
145 
146 	delete bmp;
147 	return stat;
148 }
149 
150 /*!
151 	\brief Removes the data associated with the specifier from the cursor set
152 	\param which System cursor specifier defined in CursorSet.h
153 */
154 void
155 CursorSet::RemoveCursor(cursor_which which)
156 {
157 	RemoveData(CursorWhichToString(which));
158 }
159 
160 /*!
161 	\brief Retrieves a cursor from the set.
162 	\param which System cursor specifier defined in CursorSet.h
163 	\param cursor Bitmap** to receive a newly-allocated BBitmap containing the appropriate data
164 	\param hotspot The recipient of the hotspot for the cursor
165 	\return
166 	- \c B_OK: Success
167 	- \c B_BAD_VALUE: a NULL parameter was passed
168 	- \c B_NAME_NOT_FOUND: The specified cursor does not exist in this set
169 	- \c B_ERROR: An internal error occurred
170 
171 	BBitmaps created by this function are the responsibility of the caller.
172 */
173 status_t
174 CursorSet::FindCursor(cursor_which which, BBitmap **cursor, BPoint *hotspot)
175 {
176 	if (!cursor || !hotspot);
177 		return B_BAD_VALUE;
178 
179 	BMessage msg;
180 	if (FindMessage(CursorWhichToString(which), &msg) != B_OK)
181 		return B_NAME_NOT_FOUND;
182 
183 	const void *buffer;
184 	const char *tempstr;
185 	int32 bufferLength;
186 	BBitmap *bmp;
187 	BPoint hotpt;
188 
189 	if (msg.FindString("class", &tempstr) != B_OK)
190 		return B_ERROR;
191 
192 	if (msg.FindPoint("hotspot", &hotpt) != B_OK)
193 		return B_ERROR;
194 
195 	if (strcmp(tempstr, "cursor") == 0) {
196 		bmp = new BBitmap(msg.FindRect("_frame"),
197 			(color_space)msg.FindInt32("_cspace"), true);
198 		msg.FindData("_data", B_RAW_TYPE, (const void **)&buffer,
199 			(ssize_t *)&bufferLength);
200 		memcpy(bmp->Bits(), buffer, bufferLength);
201 
202 		*cursor = bmp;
203 		*hotspot = hotpt;
204 		return B_OK;
205 	}
206 	return B_ERROR;
207 }
208 
209 /*!
210 	\brief Retrieves a cursor from the set.
211 	\param which System cursor specifier defined in CursorSet.h
212 	\param cursor ServerCursor** to receive a newly-allocated ServerCursor containing the appropriate data
213 	\return
214 	- \c B_OK: Success
215 	- \c B_BAD_VALUE: a NULL parameter was passed
216 	- \c B_NAME_NOT_FOUND: The specified cursor does not exist in this set
217 	- \c B_ERROR: An internal error occurred
218 
219 	BBitmaps created by this function are the responsibility of the caller.
220 */
221 status_t
222 CursorSet::FindCursor(cursor_which which, ServerCursor **_cursor)
223 {
224 	BMessage msg;
225 	if (FindMessage(CursorWhichToString(which), &msg) != B_OK)
226 		return B_NAME_NOT_FOUND;
227 
228 	const char *className;
229 	BPoint hotspot;
230 
231 	if (msg.FindString("class", &className) != B_OK)
232 		return B_ERROR;
233 
234 	if (msg.FindPoint("hotspot", &hotspot) != B_OK)
235 		return B_ERROR;
236 
237 	if (strcmp(className, "cursor") == 0) {
238 		ServerCursor *cursor = new ServerCursor(msg.FindRect("_frame"),
239 			(color_space)msg.FindInt32("_cspace"), 0, hotspot);
240 
241 		const void *buffer;
242 		int32 bufferLength;
243 		msg.FindData("_data",B_RAW_TYPE, (const void **)&buffer,
244 			(ssize_t *)&bufferLength);
245 		memcpy(cursor->Bits(), buffer, bufferLength);
246 
247 		*_cursor = cursor;
248 		return B_OK;
249 	}
250 	return B_ERROR;
251 }
252 
253 /*!
254 	\brief Returns the name of the set
255 	\return The name of the set
256 */
257 const char *
258 CursorSet::GetName(void)
259 {
260 	const char *name;
261 	if (FindString("name", &name) == B_OK)
262 		return name;
263 
264 	return NULL;
265 }
266 
267 /*!
268 	\brief Renames the cursor set
269 	\param name new name of the set.
270 
271 	This function will fail if given a NULL name
272 */
273 void
274 CursorSet::SetName(const char *name)
275 {
276 	if (!name)
277 		return;
278 
279 	RemoveData("name");
280 	AddString("name", name);
281 }
282 
283 
284 /*!
285 	\brief Returns a string for the specified cursor attribute
286 	\param which System cursor specifier defined in CursorSet.h
287 	\return Name for the cursor specifier
288 */
289 const char *
290 CursorWhichToString(cursor_which which)
291 {
292 	switch (which) {
293 		case B_CURSOR_DEFAULT:
294 			return "Default";
295 		case B_CURSOR_TEXT:
296 			return "Text";
297 		case B_CURSOR_MOVE:
298 			return "Move";
299 		case B_CURSOR_DRAG:
300 			return "Drag";
301 		case B_CURSOR_RESIZE:
302 			return "Resize";
303 		case B_CURSOR_RESIZE_NWSE:
304 			return "Resize NWSE";
305 		case B_CURSOR_RESIZE_NESW:
306 			return "Resize NESW";
307 		case B_CURSOR_RESIZE_NS:
308 			return "Resize NS";
309 		case B_CURSOR_RESIZE_EW:
310 			return "Resize EW";
311 		case B_CURSOR_OTHER:
312 			return "Other";
313 		default:
314 			return "Invalid";
315 	}
316 }
317 
318 /*!
319 	\brief Creates a new BBitmap from R5 cursor data
320 	\param data Pointer to data in the R5 cursor data format
321 	\return NULL if data was NULL, otherwise a new BBitmap
322 
323 	BBitmaps returned by this function are always in the RGBA32 color space
324 */
325 BBitmap *
326 CursorDataToBitmap(int8 *data)
327 {
328 	// 68-byte array used in R5 for holding cursors.
329 	// This API has serious problems and should be deprecated(but supported) in R2
330 
331 	// Now that we have all the setup, we're going to map (for now) the cursor
332 	// to RGBA32. Eventually, there will be support for 16 and 8-bit depths
333 	if (data) {
334 	 	BBitmap *bmp = new BBitmap(BRect(0,0,15,15),B_RGBA32,0);
335 		uint32 black = 0xFF000000, white=0xFFFFFFFF, *bmppos;
336 		uint16 *cursorpos, *maskpos, cursorflip, maskflip;
337 		uint16 cursorval, maskval, powval;
338 		uint8 i, j, *_buffer;
339 
340 		_buffer=(uint8*)bmp->Bits();
341 		cursorpos=(uint16*)(data+4);
342 		maskpos=(uint16*)(data+36);
343 
344 		// for each row in the cursor data
345 		for (j = 0; j < 16; j++) {
346 			bmppos = (uint32*)(_buffer+ (j*bmp->BytesPerRow()) );
347 
348 			// On intel, our bytes end up swapped, so we must swap them back
349 			cursorflip = (cursorpos[j] & 0xFF) << 8;
350 			cursorflip |= (cursorpos[j] & 0xFF00) >> 8;
351 
352 			maskflip = (maskpos[j] & 0xFF) << 8;
353 			maskflip |= (maskpos[j] & 0xFF00) >> 8;
354 
355 			// for each column in each row of cursor data
356 			for (i = 0; i < 16; i++) {
357 				// Get the values and dump them to the bitmap
358 				powval = 1 << (15-i);
359 				cursorval = cursorflip & powval;
360 				maskval = maskflip & powval;
361 				bmppos[i] = (cursorval != 0 ? black : white)
362 					& (maskval > 0 ? 0xFFFFFFFF : 0x00FFFFFF);
363 			}
364 		}
365 		return bmp;
366 	}
367 
368 	return NULL;
369 }
370