diff --git a/src/servers/app/server/SysCursor.cpp b/src/servers/app/server/SysCursor.cpp new file mode 100644 index 0000000000..9aba962ebf --- /dev/null +++ b/src/servers/app/server/SysCursor.cpp @@ -0,0 +1,444 @@ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: SysCursor.cpp +// Author: DarkWyrm +// Description: Private file encapsulating OBOS system cursor API +// +//------------------------------------------------------------------------------ +#include +#include +#include +#include +#include +#include +#include "SysCursor.h" +#include "ServerCursor.h" + +/*! + \brief Sets a system cursor + \param which System cursor specifier defined in SysCursor.h + \param cursor The new cursor +*/ +void set_syscursor(cursor_which which, const BCursor *cursor) +{ + port_id server=find_port(SERVER_PORT_NAME); + if(server!=B_NAME_NOT_FOUND) + { + PortLink link(server); + link.SetOpCode(AS_SET_SYSCURSOR_BCURSOR); + link.Attach(which); + + // The easy (and clean) way for us to access the cursor's token + // would be to make it a friend function of the BCursor class. One problem: + // we couldn't build this under R5. For R1, we'll use a hack which we can + // get away with because it has to be binary compatible. + int32 *hack=(int32*)cursor; + link.Attach(hack[0]); +// link.Attach(cursor->m_serverToken); + + link.Flush(); + } +} + +/*! + \brief Sets a system cursor + \param which System cursor specifier defined in SysCursor.h + \param bitmap BBitmap to represent the new cursor. Size should be 48x48 or less. +*/ +void set_syscursor(cursor_which which, const BBitmap *bitmap) +{ + port_id server=find_port(SERVER_PORT_NAME); + if(server!=B_NAME_NOT_FOUND) + { + PortLink link(server); + link.SetOpCode(AS_SET_SYSCURSOR_BBITMAP); + link.Attach(which); + + // Just like the BCursor version, we will use a hack until R1. + int32 *hack=(int32*)bitmap; + hack+=(sizeof(int32)*4)+sizeof(color_space)+sizeof(BRect); + link.Attach(hack[0]); + + link.Flush(); + } +} + +/*! + \brief Returns the cursor specifier currently shown + \return Returns B_CURSOR_OTHER if an application-set cursor. Otherwise, see SysCursor.h +*/ +cursor_which get_syscursor(void) +{ + port_id server=find_port(SERVER_PORT_NAME); + if(server!=B_NAME_NOT_FOUND) + { + PortMessage pmsg; + + PortLink link(server); + link.SetOpCode(AS_GET_SYSCURSOR); + link.FlushWithReply(&pmsg); + + cursor_which which; + pmsg.Read(&which); + return which; + } + return B_CURSOR_INVALID; +} + +/*! + \brief Changes the application cursor to the specified cursor + \param which System cursor specifier defined in SysCursor.h +*/ +void setcursor(cursor_which which) +{ + port_id server=find_port(SERVER_PORT_NAME); + if(server!=B_NAME_NOT_FOUND) + { + PortLink link(server); + link.SetOpCode(AS_SET_CURSOR_SYSTEM); + link.Flush(); + } +} + +/*! + \brief Constructor + \name Name of the cursor set. +*/ +CursorSet::CursorSet(const char *name) + : BMessage() +{ + AddString("name",(name)?name:"Untitled"); +} + +/*! + \brief Saves the data in the cursor set to a file + \param path Path of the file to save to. + \param saveflags BFile open file flags. B_READ_WRITE is implied. + \return + - \c B_OK: Everything went fine. + - \c B_BAD_VALUE: path is NULL + - \c other value: See BFile::SetTo and BMessage::Flatten return codes +*/ +status_t CursorSet::Save(const char *path,int32 saveflags) +{ + if(!path) + return B_BAD_VALUE; + status_t stat=B_OK; + + BFile file; + stat=file.SetTo(path,B_READ_WRITE | saveflags); + if(stat!=B_OK) + return stat; + + stat=Flatten(&file); + + return stat; +} + +/*! + \brief Loads the data into the cursor set from a file + \param path Path of the file to load from. + \return + - \c B_OK: Everything went fine. + - \c B_BAD_VALUE: path is NULL + - \c other value: See BFile::SetTo and BMessage::Flatten return codes +*/ +status_t CursorSet::Load(const char *path) +{ + if(!path) + return B_BAD_VALUE; + status_t stat=B_OK; + + BFile file; + stat=file.SetTo(path, B_READ_ONLY); + if(stat!=B_OK) + return stat; + + stat=Unflatten(&file); + + return stat; +} + +/*! + \brief Adds the cursor to the set and replaces any existing entry for the given specifier + \param which System cursor specifier defined in SysCursor.h + \param cursor BBitmap to represent the new cursor. Size should be 48x48 or less. + \param hotspot The recipient of the hotspot for the cursor + \return B_BAD_VALUE if cursor is NULL, otherwise B_OK +*/ +status_t CursorSet::AddCursor(cursor_which which,const BBitmap *cursor, const BPoint &hotspot) +{ + if(!cursor) + return B_BAD_VALUE; + + // Remove the data if it exists already + RemoveData(CursorWhichToString(which)); + + // Actually add the data to our set + BMessage msg((int32)which); + + msg.AddString("class","bitmap"); + msg.AddRect("_frame",cursor->Bounds()); + msg.AddInt32("_cspace",cursor->ColorSpace()); + + msg.AddInt32("_bmflags",0); + msg.AddInt32("_rowbytes",cursor->BytesPerRow()); + msg.AddPoint("hotspot",hotspot); + msg.AddData("_data",B_RAW_TYPE,cursor->Bits(), cursor->BitsLength()); + AddMessage(CursorWhichToString(which),&msg); + + return B_OK; +} + +/*! + \brief Adds the cursor to the set and replaces any existing entry for the given specifier + \param which System cursor specifier defined in SysCursor.h + \param data R5 cursor data pointer + \return B_BAD_VALUE if data is NULL, otherwise B_OK + + When possible, it is better to use the BBitmap version of AddCursor because this + function must convert the R5 cursor data into a BBitmap +*/ +status_t CursorSet::AddCursor(cursor_which which, int8 *data) +{ + // Convert cursor data to a bitmap because all cursors are internally stored + // as bitmaps + if(!data) + return B_BAD_VALUE; + + BBitmap *bmp=CursorDataToBitmap(data); + BPoint pt(data[2],data[3]); + + status_t stat=AddCursor(which,bmp,pt); + + if(bmp) + delete bmp; + + return stat; +} + +/*! + \brief Removes the data associated with the specifier from the cursor set + \param which System cursor specifier defined in SysCursor.h +*/ +void CursorSet::RemoveCursor(cursor_which which) +{ + RemoveData(CursorWhichToString(which)); +} + +/*! + \brief Retrieves a cursor from the set. + \param which System cursor specifier defined in SysCursor.h + \param cursor Bitmap** to receive a newly-allocated BBitmap containing the appropriate data + \param hotspot The recipient of the hotspot for the cursor + \return + - \c B_OK: Success + - \c B_BAD_VALUE: a NULL parameter was passed + - \c B_NAME_NOT_FOUND: The specified cursor does not exist in this set + - \c B_ERROR: An internal error occurred + + BBitmaps created by this function are the responsibility of the caller. +*/ +status_t CursorSet::FindCursor(cursor_which which, BBitmap **cursor, BPoint *hotspot) +{ + + if(!cursor || !hotspot); + return B_BAD_VALUE; + + BMessage msg; + + if(FindMessage(CursorWhichToString(which),&msg)!=B_OK) + return B_NAME_NOT_FOUND; + + const void *buffer; + BString tempstr; + int32 bufferLength; + BBitmap *bmp; + BPoint hotpt; + + if(msg.FindString("class",&tempstr)!=B_OK) + return B_ERROR; + + if(msg.FindPoint("hotspot",&hotpt)!=B_OK) + return B_ERROR; + + + if(tempstr.Compare("cursor")==0) + { + bmp=new BBitmap( msg.FindRect("_frame"), + (color_space) msg.FindInt32("_cspace"),true ); + msg.FindData("_data",B_RAW_TYPE,(const void **)&buffer, &bufferLength); + memcpy(bmp->Bits(), buffer, bufferLength); + + *cursor=bmp; + *hotspot=hotpt; + return B_OK; + } + return B_ERROR; +} + +/*! + \brief Retrieves a cursor from the set. + \param which System cursor specifier defined in SysCursor.h + \param cursor ServerCursor** to receive a newly-allocated ServerCursor containing the appropriate data + \return + - \c B_OK: Success + - \c B_BAD_VALUE: a NULL parameter was passed + - \c B_NAME_NOT_FOUND: The specified cursor does not exist in this set + - \c B_ERROR: An internal error occurred + + BBitmaps created by this function are the responsibility of the caller. +*/ +status_t CursorSet::FindCursor(cursor_which which, ServerCursor **cursor) +{ + BMessage msg; + + if(FindMessage(CursorWhichToString(which),&msg)!=B_OK) + return B_NAME_NOT_FOUND; + + const void *buffer; + BString tempstr; + int32 bufferLength; + ServerCursor *csr; + BPoint hotpt; + + if(msg.FindString("class",&tempstr)!=B_OK) + return B_ERROR; + + if(msg.FindPoint("hotspot",&hotpt)!=B_OK) + return B_ERROR; + + if(tempstr.Compare("cursor")==0) + { + csr=new ServerCursor(msg.FindRect("_frame"),(color_space) msg.FindInt32("_cspace"),0, hotpt); + msg.FindData("_data",B_RAW_TYPE,(const void **)&buffer, &bufferLength); + memcpy(csr->Bits(), buffer, bufferLength); + + *cursor=csr; + return B_OK; + } + return B_ERROR; +} + +/*! + \brief Renames the cursor set + \param name new name of the set. + + This function will fail if given a NULL name +*/ +void CursorSet::Rename(const char *name) +{ + if(!name) + return; + RemoveData("name"); + AddString("name",name); +} + + +/*! + \brief Returns a string for the specified cursor attribute + \param which System cursor specifier defined in SysCursor.h + \return Name for the cursor specifier +*/ +const char *CursorWhichToString(cursor_which which) +{ + switch(which) + { + case B_CURSOR_DEFAULT: + return "Default"; + case B_CURSOR_TEXT: + return "Text"; + case B_CURSOR_MOVE: + return "Move"; + case B_CURSOR_DRAG: + return "Drag"; + case B_CURSOR_RESIZE: + return "Resize"; + case B_CURSOR_RESIZE_NWSE: + return "Resize NWSE"; + case B_CURSOR_RESIZE_NESW: + return "Resize NESW"; + case B_CURSOR_RESIZE_NS: + return "Resize NS"; + case B_CURSOR_RESIZE_EW: + return "Resize EW"; + case B_CURSOR_OTHER: + return "Other"; + default: + return "Invalid"; + } +} + +/*! + \brief Creates a new BBitmap from R5 cursor data + \param data Pointer to data in the R5 cursor data format + \return NULL if data was NULL, otherwise a new BBitmap + + BBitmaps returned by this function are always in the RGBA32 color space +*/ +BBitmap *CursorDataToBitmap(int8 *data) +{ + // 68-byte array used in R5 for holding cursors. + // This API has serious problems and should be deprecated(but supported) in R2 + + // Now that we have all the setup, we're going to map (for now) the cursor + // to RGBA32. Eventually, there will be support for 16 and 8-bit depths + if(data) + { + BBitmap *bmp=new BBitmap(BRect(0,0,15,15),B_RGBA32,0); + uint32 black=0xFF000000, + white=0xFFFFFFFF, + *bmppos; + uint16 *cursorpos, *maskpos,cursorflip, maskflip, + cursorval, maskval,powval; + uint8 i,j,*_buffer; + _buffer=(uint8*)bmp->Bits(); + cursorpos=(uint16*)(data+4); + maskpos=(uint16*)(data+36); + + // for each row in the cursor data + for(j=0;j<16;j++) + { + bmppos=(uint32*)(_buffer+ (j*bmp->BytesPerRow()) ); + + // On intel, our bytes end up swapped, so we must swap them back + cursorflip=(cursorpos[j] & 0xFF) << 8; + cursorflip |= (cursorpos[j] & 0xFF00) >> 8; + + maskflip=(maskpos[j] & 0xFF) << 8; + maskflip |= (maskpos[j] & 0xFF00) >> 8; + + // for each column in each row of cursor data + for(i=0;i<16;i++) + { + // Get the values and dump them to the bitmap + powval=1 << (15-i); + cursorval=cursorflip & powval; + maskval=maskflip & powval; + bmppos[i]=((cursorval!=0)?black:white) & ((maskval>0)?0xFFFFFFFF:0x00FFFFFF); + } + } + return bmp; + } + + return NULL; +}