void GetCursorInfo() { CGSConnectionRef connection = getConnection(); CGError err = noErr; int cursorDataSize, depth, components, bitsPerComponent, cursorRowSize; unsigned char* cursorData; CGPoint location, hotspot; CGRect cursorRect; int i, j; err = CGSGetCurrentCursorLocation(connection, &location); printf("location (err %d) = %d, %d\n", err, (int)location.x, (int)location.y); err = CGSGetGlobalCursorDataSize(connection, &cursorDataSize); printf("data size (err %d) = %d\n", err, cursorDataSize); cursorData = (unsigned char*)calloc(cursorDataSize, sizeof(unsigned char)); err = CGSGetGlobalCursorData(connection, cursorData, &cursorDataSize, &cursorRowSize, &cursorRect, &hotspot, &depth, &components, &bitsPerComponent); printf("rect origin (%g, %g), dimensions (%g, %g)\n", cursorRect.origin.x, cursorRect.origin.y, cursorRect.size.width, cursorRect.size.height); printf("hotspot (%g, %g)\n", hotspot.x, hotspot.y); printf("depth: %d\n", depth); printf("components: %d\n", components); printf("bits per component: %d\n", bitsPerComponent); printf("Bytes Per Row: %d\n", cursorRowSize); printf("Components (err %d):\n", err); // Print Colors for (j=0; j < components; j++) { printf("\n"); for (i=0; i < cursorDataSize; i++) { if (i % cursorRowSize == 0) printf("\n"); if (i % components == j) printf("%02x", (int)cursorData[i]); } } printf("released connection (err %d)\n", CGSReleaseConnection(connection)); }
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; }
void loadCurrentCursorData() { CGError err; CGSConnectionRef connection = getConnection(); if (!connection) { if (!maxFailsRemaining) return; } if (CGSGetGlobalCursorDataSize(connection, &cursorDataSize) != kCGErrorSuccess) { rfbLog("Error obtaining cursor data - cursor not sent\n"); return; } if (cursorData) free(cursorData); cursorData = (unsigned char*)malloc(sizeof(unsigned char) * cursorDataSize); err = CGSGetGlobalCursorData(connection, cursorData, &cursorDataSize, &cursorRowBytes, &cursorRect, &hotspot, &cursorDepth, &components, &cursorBitsPerComponent); //CGSReleaseConnection(connection); if (err != kCGErrorSuccess) { free(cursorData); cursorData = NULL; rfbLog("Error obtaining cursor data - cursor not sent\n"); return; } 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); cursorMaskSize = floor((cursorRect.size.width+7)/8) * cursorRect.size.height; if (cursorMaskData) free(cursorMaskData); cursorMaskData = (unsigned char*)malloc(sizeof(unsigned char) * cursorMaskSize); // 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 // For starters we'll set mask to OFF (transparent) everywhere) memset(cursorMaskData, 0, cursorMaskSize); // This algorithm assumes the Alpha channel is the first component { unsigned char *maskPointer = cursorMaskData; 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) maskPointer[(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 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; }
block_t *screen_Capture( demux_t *p_demux ) { demux_sys_t *p_sys = p_demux->p_sys; screen_data_t *p_data = ( screen_data_t * )p_sys->p_data; block_t *p_block; int i_size; i_size = p_sys->fmt.video.i_height * p_sys->fmt.video.i_width * 4; if( !( p_block = block_New( p_demux, i_size ) ) ) { msg_Warn( p_demux, "cannot get block" ); return NULL; } CGPoint cursor_pos; CGError cursor_result; cursor_pos.x = 0; cursor_pos.y = 0; cursor_result = CGSGetCurrentCursorLocation( p_data->connection, &cursor_pos ); if( p_sys->b_follow_mouse && cursor_result == kCGErrorSuccess ) { FollowMouse( p_sys, cursor_pos.x, cursor_pos.y ); p_data->left = p_sys->i_left; p_data->top = p_sys->i_top; } CGLSetCurrentContext( p_data->screen ); glReadPixels( p_data->left, p_data->screen_height - p_data->top - p_data->src_height, p_data->src_width, p_data->src_height, GL_RGBA, GL_UNSIGNED_BYTE, p_data->texture_image ); CGLSetCurrentContext( p_data->scaled ); glEnable( GL_TEXTURE_2D ); glBindTexture( GL_TEXTURE_2D, p_data->texture ); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, p_data->src_width, p_data->src_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, p_data->texture_image ); glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); glClear( GL_COLOR_BUFFER_BIT ); glColor3f( 1.0f, 1.0f, 1.0f ); glEnable( GL_TEXTURE_2D ); glBindTexture( GL_TEXTURE_2D, p_data->texture ); glBegin( GL_POLYGON ); glTexCoord2f( 0.0, 1.0 ); glVertex2f( -1.0, -1.0 ); glTexCoord2f( 1.0, 1.0 ); glVertex2f( 1.0, -1.0 ); glTexCoord2f( 1.0, 0.0 ); glVertex2f( 1.0, 1.0 ); glTexCoord2f( 0.0, 0.0 ); glVertex2f( -1.0, 1.0 ); glEnd(); glDisable( GL_TEXTURE_2D ); int size; int tmp1, tmp2, tmp3, tmp4; unsigned char *cursor_image; CGRect cursor_rect; CGPoint cursor_hot; if( cursor_result == kCGErrorSuccess && CGSGetGlobalCursorDataSize( p_data->connection, &size ) == kCGErrorSuccess ) { cursor_image = ( unsigned char* )malloc( size ); if( CGSGetGlobalCursorData( p_data->connection, cursor_image, &size, &tmp1, &cursor_rect, &cursor_hot, &tmp2, &tmp3, &tmp4 ) == kCGErrorSuccess ) { glEnable( GL_TEXTURE_2D ); glBindTexture( GL_TEXTURE_2D, p_data->cursor_texture ); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, ( int )( cursor_rect.size.width ), ( int )( cursor_rect.size.height ), 0, GL_RGBA, GL_UNSIGNED_BYTE, ( char * )cursor_image ); cursor_rect.origin.x = cursor_pos.x - p_data->left - cursor_hot.x; cursor_rect.origin.y = cursor_pos.y - p_data->top - cursor_hot.y; cursor_rect.origin.x = 2.0 * cursor_rect.origin.x / p_data->src_width - 1.0; cursor_rect.origin.y = 2.0 * cursor_rect.origin.y / p_data->src_height - 1.0; cursor_rect.size.width = 2.0 * cursor_rect.size.width / p_data->src_width; cursor_rect.size.height = 2.0 * cursor_rect.size.height / p_data->src_height; 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( cursor_rect.origin.x, cursor_rect.origin.y ); glTexCoord2f( 1.0, 0.0 ); glVertex2f( cursor_rect.origin.x + cursor_rect.size.width, cursor_rect.origin.y ); glTexCoord2f( 1.0, 1.0 ); glVertex2f( cursor_rect.origin.x + cursor_rect.size.width, cursor_rect.origin.y + cursor_rect.size.height ); glTexCoord2f( 0.0, 1.0 ); glVertex2f( cursor_rect.origin.x, cursor_rect.origin.y + cursor_rect.size.height ); glEnd(); glDisable( GL_BLEND ); glDisable( GL_TEXTURE_2D ); } free( cursor_image ); } glReadPixels( 0, 0, p_data->dest_width, p_data->dest_height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, p_block->p_buffer ); return p_block; }