// We call this to see if we have a new cursor and should notify clients to do an update // Or if cursor has moved void rfbCheckForCursorChange() { CGPoint cursorLoc = currentCursorLoc(); //rfbLog("Check For Cursor Change"); // First Let's see if we have new info on the pasteboard - if so we'll send an update to each client if (lastCursorSeed != CGSCurrentCursorSeed() || !CGPointEqualToPoint(lastCursorPosition, cursorLoc)) { rfbClientIteratorPtr iterator = rfbGetClientIterator(); rfbClientPtr cl; // Record first in case another change occurs after notifying clients lastCursorSeed = CGSCurrentCursorSeed(); lastCursorPosition = cursorLoc; // Notify each client while ((cl = rfbClientIteratorNext(iterator)) != NULL) { if (rfbShouldSendNewCursor(cl) || (rfbShouldSendNewPosition(cl))) pthread_cond_signal(&cl->updateCond); } rfbReleaseClientIterator(iterator); } }
int screen_DrawCursor( demux_sys_t *p_sys, CGPoint *cursor_pos ) { int size; int tmp1, tmp2, tmp3, tmp4; unsigned char *cursor_image; screen_data_t *p_data = p_sys->p_data; int seed = CGSCurrentCursorSeed(); if( seed != p_data->cursor_seed ) { p_data->cursor_need_update = 1; if( CGSGetGlobalCursorDataSize( p_data->connection, &size ) != kCGErrorSuccess) { return VLC_EGENERIC; } cursor_image = ( unsigned char * )malloc( size ); if( CGSGetGlobalCursorData( p_data->connection, cursor_image, &size, &tmp1, &p_data->cursor_rect, &p_data->cursor_hot, &tmp2, &tmp3, &tmp4 ) != kCGErrorSuccess ) { free( cursor_image ); return VLC_EGENERIC; } long int pot_width, pot_height; POT( pot_width, p_data->cursor_rect.size.width ); POT( pot_height, p_data->cursor_rect.size.height ); p_data->cursor_texture_map_u = p_data->cursor_rect.size.width / ( double )pot_width; p_data->cursor_texture_map_v = p_data->cursor_rect.size.height / ( double )pot_height; /* We need transparent image larger than original, * use calloc to clear alpha value to 0. */ char *pot_cursor_image = ( char * )calloc( pot_width * pot_height * 4, sizeof( char ) ); int width, height; char *from, *to; width = p_data->cursor_rect.size.width; height = p_data->cursor_rect.size.height; from = ( char * )cursor_image; to = pot_cursor_image; #ifdef __LITTLE_ENDIAN__ int y, fromwidth, towidth; fromwidth = width * 4; towidth = pot_width * 4; for( y = height; y; y -- ) { memcpy( to, from, fromwidth ); to += towidth; from += fromwidth; } #else int x, y, diff; diff = ( pot_width - width ) * 4; for( y = height; y; y -- ) { for( x = width; x; x -- ) { to[0] = from[3]; to[1] = from[2]; to[2] = from[1]; to[3] = from[0]; to += 4; from += 4; } to += diff; } #endif glEnable( GL_TEXTURE_2D ); glBindTexture( GL_TEXTURE_2D, p_data->cursor_texture ); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, pot_width, pot_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pot_cursor_image ); p_data->cursor_need_update = 0; p_data->cursor_seed = seed; free( pot_cursor_image ); free( cursor_image ); } else if( p_data->cursor_need_update ) { return VLC_EGENERIC; } double x, y; double x1, y1, x2, y2; x = cursor_pos->x - p_sys->i_left - p_data->cursor_hot.x; y = cursor_pos->y - p_sys->i_top - p_data->cursor_hot.y; x1 = 2.0 * x / p_data->width - 1.0; y1 = 2.0 * y / p_data->height - 1.0; x2 = 2.0 * ( x + p_data->cursor_rect.size.width ) / p_data->width - 1.0; y2 = 2.0 * ( y + p_data->cursor_rect.size.height ) / p_data->height - 1.0; glColor3f( 1.0f, 1.0f, 1.0f ); glEnable( GL_TEXTURE_2D ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glBindTexture( GL_TEXTURE_2D, p_data->cursor_texture ); glBegin( GL_POLYGON ); glTexCoord2f( 0.0, 0.0 ); glVertex2f( x1, y1 ); glTexCoord2f( p_data->cursor_texture_map_u, 0.0 ); glVertex2f( x2, y1 ); glTexCoord2f( p_data->cursor_texture_map_u, p_data->cursor_texture_map_v ); glVertex2f( x2, y2 ); glTexCoord2f( 0.0, p_data->cursor_texture_map_v ); glVertex2f( x1, y2 ); glEnd(); glDisable( GL_BLEND ); glDisable( GL_TEXTURE_2D ); return VLC_SUCCESS; }
Bool rfbSendRichCursorUpdate(rfbClientPtr cl) { rfbFramebufferUpdateRectHeader rect; rfbPixelFormat cursorFormat; unsigned char *cursorData; int bufferMaskOffset; int cursorSize; // Size of cursor data from size int cursorRowBytes; int cursorDataSize; // Size to be sent to client int cursorMaskSize; // Mask Size to be sent to client int cursorDepth; int cursorBitsPerComponent; BOOL cursorIsDifferentFormat = FALSE; CGError err; CGSConnectionRef connection = getConnection(); int components; // Cursor Components CGPoint hotspot; CGRect cursorRect; //rfbLog("Sending Cursor To Client"); //GetCursorInfo(); if (!connection) { if (!maxFailsRemaining) cl->useRichCursorEncoding = FALSE; return FALSE; } if (CGSGetGlobalCursorDataSize(connection, &cursorDataSize) != kCGErrorSuccess) { rfbLog("Error obtaining cursor data - cursor not sent\n"); return FALSE; } cursorData = (unsigned char*)malloc(sizeof(unsigned char) * cursorDataSize); err = CGSGetGlobalCursorData(connection, cursorData, &cursorDataSize, &cursorRowBytes, &cursorRect, &hotspot, &cursorDepth, &components, &cursorBitsPerComponent); //CGSReleaseConnection(connection); if (err != kCGErrorSuccess) { rfbLog("Error obtaining cursor data - cursor not sent\n"); return FALSE; } if (cursorRect.size.height > 128 || cursorRect.size.width > 128) { // Wow That's one big cursor! We don't handle cursors this big // (they are probably cursors with lots of states and that doesn't work so good for VNC. // For now just ignore them cl->currentCursorSeed = CGSCurrentCursorSeed(); return FALSE; } // For This We Don't send location just the cursor shape (and Hot Spot) cursorFormat.depth = (cursorDepth == 32 ? 24 : cursorDepth); cursorFormat.bitsPerPixel = cursorDepth; cursorFormat.bigEndian = TRUE; cursorFormat.trueColour = TRUE; cursorFormat.redMax = cursorFormat.greenMax = cursorFormat.blueMax = (unsigned short) ((1<<cursorBitsPerComponent) - 1); cursorFormat.bigEndian = !littleEndian; cursorFormat.redShift = (unsigned char) (cursorBitsPerComponent * 2); cursorFormat.greenShift = (unsigned char) (cursorBitsPerComponent * 1); cursorFormat.blueShift = (unsigned char) (cursorBitsPerComponent * 0); //GetCursorInfo(); //PrintPixelFormat(&cursorFormat); cursorIsDifferentFormat = !(PF_EQ(cursorFormat,rfbServerFormat)); cursorSize = (cursorRect.size.width * cursorRect.size.height * (cl->format.bitsPerPixel / 8)); cursorMaskSize = floor((cursorRect.size.width+7)/8) * cursorRect.size.height; // Make Sure we have space on the buffer (otherwise push the data out now) if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + cursorSize + cursorMaskSize > UPDATE_BUF_SIZE) { if (!rfbSendUpdateBuf(cl)) return FALSE; } // Send The Header rect.r.x = Swap16IfLE((short) hotspot.x); rect.r.y = Swap16IfLE((short) hotspot.y); rect.r.w = Swap16IfLE((short) cursorRect.size.width); rect.r.h = Swap16IfLE((short) cursorRect.size.height); rect.encoding = Swap32IfLE(rfbEncodingRichCursor); memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; // Apple Cursors can use a full Alpha channel. // Since we can only send a bit mask - to get closer we will compose the full color with a white // This requires us to jump ahead to write in the update buffer bufferMaskOffset = cl->ublen + cursorSize; // For starters we'll set mask to OFF (transparent) everywhere) memset(&cl->updateBuf[bufferMaskOffset], 0, cursorMaskSize); // This algorithm assumes the Alpha channel is the first component { unsigned char *cursorRowData = cursorData; unsigned char *cursorColumnData = cursorData; unsigned int cursorBytesPerPixel = (cursorDepth/8); unsigned char mask = 0; unsigned int alphaShift = (8 - cursorBitsPerComponent); unsigned char fullOn = (0xFF) >> alphaShift; unsigned char alphaThreshold = (0x60) >> alphaShift; // Only include the pixel if it's coverage is greater than this int dataX, dataY, componentIndex; for (dataY = 0; dataY < cursorRect.size.height; dataY++) { cursorColumnData = cursorRowData; for (dataX = 0; dataX < cursorRect.size.width; dataX++) { if (littleEndian) mask = (unsigned char)(*(cursorColumnData+(cursorBytesPerPixel-1))) >> alphaShift; else mask = (unsigned char)(*cursorColumnData) >> alphaShift; if (mask > alphaThreshold) { // Write the Bit For The Mask to be ON (opaque) cl->updateBuf[bufferMaskOffset+(dataX/8)] |= (0x0080 >> (dataX % 8)); // Composite Alpha into the cursors other channels - only for 32 bit if (cursorDepth == 32 && mask != fullOn) { for (componentIndex = 0; componentIndex < components; componentIndex++) { *cursorColumnData = (unsigned char) (fullOn - mask + ((*cursorColumnData * mask)/fullOn)) & 0xFF; cursorColumnData++; } } else cursorColumnData += cursorBytesPerPixel; } else cursorColumnData += cursorBytesPerPixel; }
Bool rfbShouldSendNewCursor(rfbClientPtr cl) { if (!cl->useRichCursorEncoding) return FALSE; else return (cl->currentCursorSeed != CGSCurrentCursorSeed()); }