Linux /dev/input/event*: lost mouse movement events - c

I wrote a simple code that in theory should track the mouse position using global pixel coordinates based on display width/height.
The application creates a window (1x1 pixel at top left display corner) only for catching "ok" keyboard buttons press to stop it and to move mouse at (0, 0) when application starts. So "write" ok in order to close the application!
When I move the mouse from left to right (or top/bottom) I always obtain a different value when I reach the edge of the screen. My screen is set at 1920x1080 but never reach 1920 when I'm on the right edge. Maybe a normalization problem but this is not the point.
The problem is that if I move the mouse to the left edge of the screen when I'm at right edge I can't reach 0 and when I go right again I don't reach the same value as before.
It seems that some events are lost and the behaviour changes if I move the mouse with higher acceleration.
This is a working code, you only have to change the variable mouseID with your based on output from ls -lh /dev/input/by-id command.
#include <stdio.h>
#include <string.h>
#include "/usr/include/linux/input.h"
#include <poll.h>
#include <fcntl.h>
#include <unistd.h>
#include "SDL.h"
typedef struct connectedMouseInfoStruct
{
struct pollfd fileDescriptors[1];
char devInputStream[96];
unsigned char eventsBuffer[640000];
short type;
short code;
int value;
int currX;
int currY;
int prevX;
int prevY;
}connectedMouseInfo;
int main(int argc, char **argv)
{
char *mouseID = "usb-PixArt_Microsoft_USB_Optical_Mouse-event-mouse";
int exit = 0;
int firstKey = 0;
char *inputEvent = "/dev/input/by-id/";
connectedMouseInfo connectedMouse = {0};
int dx = 0;
int dy = 0;
SDL_Event events;
const Uint8 *keyboardState = NULL;
int bytesRead = 0;
int bufferPosition;
// Start
SDL_InitSubSystem(SDL_INIT_VIDEO);
SDL_DisplayMode currentVideoMode;
SDL_GetCurrentDisplayMode(0, &currentVideoMode);
SDL_Window* gameWindow = NULL;
gameWindow = SDL_CreateWindow(
"MouseEvents",
0,
0,
1,
1,
SDL_WINDOW_HIDDEN);
strcat(connectedMouse.devInputStream, inputEvent);
strcat(connectedMouse.devInputStream, mouseID);
connectedMouse.fileDescriptors[0].fd = open(connectedMouse.devInputStream, O_RDONLY | O_NONBLOCK);
connectedMouse.fileDescriptors[0].events = POLLIN;
SDL_WarpMouseGlobal(0, 0);
while (!exit)
{
while (SDL_PollEvent(&events))
{
keyboardState = SDL_GetKeyboardState(NULL);
if (events.type == SDL_KEYDOWN)
{
if (events.key.keysym.scancode == SDL_SCANCODE_O)
{
firstKey = 1;
}
}
if (firstKey && events.key.keysym.scancode == SDL_SCANCODE_K)
{
exit = 1;
}
}
poll(&connectedMouse.fileDescriptors[0], 1, 0);
if (connectedMouse.fileDescriptors[0].revents == POLLIN)
{
bytesRead = 0;
bytesRead = read(connectedMouse.fileDescriptors[0].fd, connectedMouse.eventsBuffer, 640000);
if (bytesRead == -1)
printf("Read error!!!\n");
else if ((bytesRead % sizeof(struct input_event)) != 0)
printf("Incomplete packet!!!\n");
else
{
printf("Read Bytes: %d\n", bytesRead);
for (bufferPosition = 0; bufferPosition < bytesRead; bufferPosition += sizeof(struct input_event))
{
memcpy(&connectedMouse.type, &connectedMouse.eventsBuffer[bufferPosition + sizeof(struct input_event) - 8], 2);
memcpy(&connectedMouse.code, &connectedMouse.eventsBuffer[bufferPosition + sizeof(struct input_event) - 6], 2);
memcpy(&connectedMouse.value, &connectedMouse.eventsBuffer[bufferPosition + sizeof(struct input_event) - 4], 4);
if (connectedMouse.type == EV_REL)
{
if (connectedMouse.code == REL_X)
{
connectedMouse.currX += connectedMouse.value;
}
else if (connectedMouse.code == REL_Y)
{
connectedMouse.currY += connectedMouse.value;
}
}
}
}
}
if (connectedMouse.currX > currentVideoMode.w - 1)
connectedMouse.currX = currentVideoMode.w - 1;
else if (connectedMouse.currX < 0)
connectedMouse.currX = 0;
if (connectedMouse.currY > currentVideoMode.h - 1)
connectedMouse.currY = currentVideoMode.h - 1;
else if (connectedMouse.currY < 0)
connectedMouse.currY = 0;
dx = connectedMouse.currX - connectedMouse.prevX;
dy = connectedMouse.currY - connectedMouse.prevY;
if (dx != 0 || dy != 0)
{
printf("Display X: %d\n", connectedMouse.currX);
printf("Display Y: %d\n", connectedMouse.currY);
printf("Delta X: %d\n", dx);
printf("Delta Y: %d\n", dy);
printf("\n");
}
connectedMouse.prevX = connectedMouse.currX;
connectedMouse.prevY = connectedMouse.currY;
}
return 0;
}

I don't think you are actually losing mouse events. The position of the cursor on the screen just doesn't match the position you keep track of because of mouse acceleration by the X server. If you turn that off, you will notice the mouse moves very slowly, and the values in your code will increase rapidly. So much so, that you will only cover a portion of the screen - but consistently. This is because the coordinate space is larger than your screen resolution.
Furthermore, you should not adjust the value on receiving an EV_SYN event. See the documentation.

I found a solution: if we set a flat profile for pointer acceleration the coordinates shown by the code exactly match the display width/height. I tried to move the pointer over a well defined pixel on my Desktop background and I always receive the same value.
So the problem was the mouse acceleration profile

Related

ncurses new_item() failure

I am quite new to ncurses and I have been trying to implement a scrolling menu by following the documentation here. My code is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ncurses.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdbool.h>
#include <menu.h>
int NUMBER_OF_TRACKS = 0;
struct Track {
char *title;
char *pathName;
}; typedef struct Track Track;
/*
* playSong is responsible for playing the selected track.
* This function utilizes fork and execvp to accomplish this.
* could maybe use popen again?
*/
void playSong(char *title) {
FILE *fp;
char cmdBuffer[1024];
sprintf(cmdBuffer, "cd ~/Music/iTunes/\"iTunes Media\"/Music/ && afplay %s", title);
fp = popen(cmdBuffer, "r");
if(fp == NULL) {
printf("Unable to play track.\n");
exit(1);
}
// close the pipe
if(pclose(fp) == -1) {
printf("Error while closing pipe.\n");
exit(1);
}
}
/*
* escapeString takes in a path name for a track, and adds escape characters
* to it so that it later be played by the system.
*/
char * escapeString(char *pathName) {
char * escape = (char *)malloc(1024 * sizeof(char));
int index = 0;
for(int i = 0; pathName[i] != '\n'; i++) {
if(pathName[i] == '/') {
if(i == 1) {
// we don't need to doubly pad the first '/' with quotes
escape[index++] = pathName[i];
escape[index++] = '\"';
}
else {
escape[index++] = '\"';
escape[index++] = pathName[i];
escape[index++] = '\"';
}
}
else {
escape[index++] = pathName[i];
}
}
// don't forget to add last quote to end the song
escape[index++] = '\"';
// don't forget null!
escape[index] = '\0';
return escape;
}
/*
* getTrackTitle parses the escaped pathame and returns only the name of the
* song.
*/
char * getTrackTitle(char *pathName) {
char *title = (char *)malloc(1024*sizeof(char));
int slashCount = 0;
int index = 0;
for(int i = 0; pathName[i] != '\0'; i++) {
if(slashCount == 3) {
title[index++] = pathName[i];
}
else if(pathName[i] == '/') {
slashCount++;
}
}
title[index] = '\0';
return title;
}
Track ** getUserTracks() {
FILE *fp;
char pathName[300]; // may need to be longer?
// use fp with popen
char *cmd = "cd ~/Music/iTunes/\"iTunes Media\"/Music/ && find . -name \"*.m4a\"";
fp = popen(cmd, "r");
if(fp == NULL) {
printf("Unable to find your tracks!\n");
exit(1);
}
// create our list
Track **trackList = (Track **)malloc(1024 * sizeof(Track *));
// read the output (i.e) the tracks
while(fgets(pathName, sizeof(pathName), fp) != NULL) {
// allocate memory for track
trackList[NUMBER_OF_TRACKS] = (Track *)malloc(sizeof(Track));
// get the escaped path name
char *escapedPath = escapeString(pathName);
// store values in struct
trackList[NUMBER_OF_TRACKS]->pathName = escapedPath;
trackList[NUMBER_OF_TRACKS]->title = getTrackTitle(escapedPath);
// update number of tracks
NUMBER_OF_TRACKS++;
}
// null terminate list
trackList[NUMBER_OF_TRACKS] = (Track *)NULL;
// close the pipe
if(pclose(fp) == -1) {
printf("Error while closing pipe.\n");
exit(1);
}
// return our list
return trackList;
}
int main(int argc, char **argv) {
/* START NCURSES */
initscr(); // create screen
curs_set(0); // hide cursor
noecho(); // don't show the characters the user is typing
cbreak(); // allow user to quit with ctrl-c; may want to remove this later
// get screen size
int yMax, xMax;
getmaxyx(stdscr, yMax, xMax);
ITEM **userTracks;
MENU *trackMenu;
WINDOW *trackMenuWindow;
// create items for our menu
Track **choices = getUserTracks();
userTracks = (ITEM **)calloc(NUMBER_OF_TRACKS+1, sizeof(ITEM *));
for(int i = 0; i < NUMBER_OF_TRACKS; i++) {
ITEM *trackItem = new_item(choices[i]->title, "");
userTracks[i] = trackItem;
}
userTracks[NUMBER_OF_TRACKS] = (ITEM *)NULL;
/* MENU STUFF */
// create the menu
trackMenu = new_menu((ITEM **)userTracks);
// printf("Item count: %d\n", item_count(trackMenu));
/* create the window associated with the menu */
trackMenuWindow = newwin(yMax, xMax, 0, 0);
box(trackMenuWindow, 0, 0); // adds border to window
keypad(trackMenuWindow, TRUE); // enables keypd use on menu
/* set main window and set sub windows */
set_menu_win(trackMenu, trackMenuWindow);
set_menu_sub(trackMenu, derwin(trackMenuWindow, yMax - 1, xMax - 1, 1, 1));
set_menu_format(trackMenu, 5, 1); // sets number of items to be displayed
/* Set menu mark to the string " * " */
set_menu_mark(trackMenu, NULL);
/* Post the menu */
post_menu(trackMenu);
wrefresh(trackMenuWindow);
int keyPressed;
while((keyPressed = wgetch(trackMenuWindow)) != 'q') {
switch(keyPressed) {
case KEY_DOWN:
menu_driver(trackMenu, REQ_DOWN_ITEM);
break;
case KEY_UP:
menu_driver(trackMenu, REQ_UP_ITEM);
break;
case KEY_NPAGE:
menu_driver(trackMenu, REQ_SCR_DPAGE);
break;
case KEY_PPAGE:
menu_driver(trackMenu, REQ_SCR_UPAGE);
break;
}
wrefresh(trackMenuWindow);
}
unpost_menu(trackMenu);
free_menu(trackMenu);
for(int i = 0; i < NUMBER_OF_TRACKS; i++) {
free_item(userTracks[i]);
}
endwin();
/* END NCURSES */
return 0;
}
Essentially, I am interested in getting a list of all the user's (iTunes) songs on their machine and displaying them in a (scrollable) menu.
I am able to retrieve all the songs and store them in my Track **choices array, however, I am not able to create any menu items from them. I have deduced that my issue lies within the followig snippet:
// create items for our menu
Track **choices = getUserTracks();
userTracks = (ITEM **)calloc(NUMBER_OF_TRACKS+1, sizeof(ITEM *));
for(int i = 0; i < NUMBER_OF_TRACKS; i++) {
ITEM *trackItem = new_item(choices[i]->title, "");
userTracks[i] = trackItem;
}
userTracks[NUMBER_OF_TRACKS] = (ITEM *)NULL;
Specifically the line ITEM *trackItem = new_item(choices[i]->title, "");
It appears as though new_item() is returning NULL in every iteration. Moreover, errno is being set to E_BAD_ARGUMENT implying that choices[i]->title is not a valid string, though I cannot imagine why this would be the case.
Any help would be immensely appreciated!
You may compile the code above as follows: gcc -I/usr/local/opt/ncurses/include -L/usr/local/opt/ncurses/lib <source_file>.c -lmenu -lncurses
You might be able to get away with excluding -I/usr/local/opt/ncurses/include -L/usr/local/opt/ncurses/lib but that unfortunately doesn't work for me.
Fair warning, this program might not work on Windows/Linux environments due to their file structure and iTunes requirement, but I have not tested it.
Turns out the strings in my structure were nonsense. I needed to allocate memory on the heap for them within my getUserTracks() function.

How to use text in EFI graphic mode?

I am super new to creating efi application. My aim is to create a small application in efi, that displays some text on a background. But I am stuck with trying to display text on the display (Great would be to have a custom font, but that is not necessary at this stage). I want the app (also) to run on apple systems (to boot from a usb)
How do I find good documentation on the EFI functions? It seems super hard to find good examples etc.
How can I display a text on a background with EFI?
This is what I got so far. I change the background to a color using the graphics protocol. How do I display a text on it. The Output String doesn't seem to work.
#include "efibind.h"
#include "efidef.h"
#include "efidevp.h"
#include "eficon.h"
#include "efiapi.h"
#include "efierr.h"
#include "efiprot.h"
static EFI_GUID GraphicsOutputProtocolGUID = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
/**
* efi_main - The entry point for the EFI application
* #image: firmware-allocated handle that identifies the image
* #SystemTable: EFI system table
*/
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systemTable) {
EFI_BOOT_SERVICES *bs = systemTable->BootServices;
EFI_STATUS status;
EFI_GRAPHICS_OUTPUT_PROTOCOL *graphicsProtocol;
SIMPLE_TEXT_OUTPUT_INTERFACE *conOut = systemTable->ConOut;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
UINTN SizeOfInfo, sWidth, sHeight;
status = bs->LocateProtocol(&GraphicsOutputProtocolGUID, NULL,
(void**)&graphicsProtocol);
if (EFI_ERROR(status) || graphicsProtocol == NULL) {
conOut->OutputString(conOut, L"Failed to init gfx!\r\n");
return status;
}
conOut->ClearScreen(conOut);
//Switch to current mode so gfx is started.
status = graphicsProtocol->SetMode(graphicsProtocol, graphicsProtocol->Mode->Mode);
if (EFI_ERROR(status)) {
conOut->OutputString(conOut, L"Failed to set default mode!\r\n");
return status;
}
EFI_GRAPHICS_OUTPUT_BLT_PIXEL p;
p.Red = 200;
p.Green = 77;
p.Blue = 13;
graphicsProtocol->QueryMode(graphicsProtocol, graphicsProtocol->Mode->Mode, &SizeOfInfo, &info);
sWidth = info->HorizontalResolution;
sHeight = info->VerticalResolution;
status = graphicsProtocol->Blt(graphicsProtocol, &p, EfiBltVideoFill, 0, 0, 0, 0, sWidth, sHeight, 0);
while (1) {
conOut->OutputString(conOut, L"Some text that I want to display\r\n");
bs->Stall(500000);
}
return EFI_SUCCESS;
}
UEFI supports graphics output. It also supports text output (which can mean either output to a serial console, or text rendered to a graphical console, or both). But there is no defined way to interact between these in a controlled manner.
Applications that provide a graphical environment with text elements (BIOS configuration menu, GRUB) generally do this using their own frameworks to draw text on the graphical console using GRAPHICS_OUTPUT_PROTOCOL.
This is a short example of a text renderer using the font module from LVGL (which can be used standalone, replace #include "../../lv_conf.h" in the lv_font.h file with #define USE_LV_FONT_DEJAVU_20 8) and the Blt method from the GRAPHICS_OUTPUT_PROTOCOL
#include <Uefi.h>
#include <Library\UefiLib.h>
#include <Protocol\GraphicsOutput.h>
#include "lv_font.h"
#define LETTER_SPACE 2
#define WAIT_SECONDS 10
#define FONT &lv_font_dejavu_20
static EFI_BOOT_SERVICES *gBS;
static EFI_RUNTIME_SERVICES *gRT;
static EFI_GRAPHICS_OUTPUT_PROTOCOL *gGOP = (EFI_GRAPHICS_OUTPUT_PROTOCOL *)NULL;
static EFI_GRAPHICS_OUTPUT_BLT_PIXEL gWhite = { 255,255,255,0 };
static void _util_render_glyph(UINT32 x, UINT32 y, CHAR8 letter)
{
UINT32 height;
UINT32 width;
UINT32 pm_x;
UINT32 pm_y;
UINT32 index;
const UINT8* bitmap;
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *pixelmap;
if (gGOP == NULL) {
return;
}
height = lv_font_get_height(FONT);
width = lv_font_get_width(FONT, letter);
// glyph is not defined in this font
if (width == 0) {
return;
}
bitmap = lv_font_get_bitmap(FONT, letter);
// using 8 bpp for simplicity
if (EFI_ERROR(gBS->AllocatePool(EfiLoaderData, height * width * sizeof(*pixelmap), (VOID**)&pixelmap))) {
return;
}
gBS->SetMem((VOID*)pixelmap, height * width * sizeof(*pixelmap), 0);
// get the current content of the framebuffer to allow 'transparent' blt operations
gGOP->Blt(gGOP, pixelmap, EfiBltVideoToBltBuffer, x, y, 0, 0, width, height, 0);
for (pm_y = 0; pm_y < height; pm_y++) {
for (pm_x = 0; pm_x < width; pm_x++) {
index = width * pm_y + pm_x;
if (bitmap[index] > 200) {
pixelmap[index].Red = 0;
pixelmap[index].Blue = 0;
pixelmap[index].Green = 0;
pixelmap[index].Reserved = 0;
}
else if (bitmap[index] > 100) {
pixelmap[index].Red = 105;
pixelmap[index].Blue = 105;
pixelmap[index].Green = 105;
pixelmap[index].Reserved = 0;
}
}
}
gGOP->Blt(gGOP, pixelmap, EfiBltBufferToVideo, 0, 0, x, y, width, height, 0);
gBS->FreePool(pixelmap);
}
static void _util_render_text(UINT32 x, UINT32 y, const CHAR8 *string)
{
UINT32 index;
UINTN length;
UINT32 scr_w;
UINT32 scr_h;
UINT32 str_x;
UINT32 gly_w;
UINT32 gly_h;
if (string == NULL) {
return;
}
if (gGOP == NULL) {
return;
}
scr_w = gGOP->Mode->Info->HorizontalResolution;
scr_h = gGOP->Mode->Info->VerticalResolution;
length = AsciiStrnLenS(string, 32);
gly_h = lv_font_get_height(FONT);
// check if the string can be printed
if ((y + gly_h) > scr_h) {
return;
}
if (x > scr_w) {
return;
}
// print the string glyph by glyph
str_x = x;
for (index = 0; index < length; index++) {
// check if the glyph can be printed
gly_w = lv_font_get_width(FONT, string[index]);
if ((str_x + gly_w) > scr_w) {
break;
}
// print the glyph
_util_render_glyph(str_x, y, string[index]);
// calculate the position of the next glyph
str_x += gly_w + LETTER_SPACE;
}
}
static void _util_fill_screen(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color)
{
if (gGOP == NULL) {
return;
}
gGOP->Blt(gGOP, color, EfiBltVideoFill, 0, 0, 0, 0, gGOP->Mode->Info->HorizontalResolution, gGOP->Mode->Info->VerticalResolution, 0);
}
static void _util_wait(UINT32 seconds)
{
EFI_TIME time;
UINT8 current_second = 255;
UINT32 elapsed_seconds = 0;
//wait for some seconds
while (elapsed_seconds <= WAIT_SECONDS) {
if (!EFI_ERROR(gRT->GetTime(&time, (EFI_TIME_CAPABILITIES*)NULL))) {
if (current_second != time.Second) {
elapsed_seconds++;
current_second = time.Second;
}
}
else {
break;
}
CpuPause();
}
}
EFI_STATUS
EFIAPI
UefiMain(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable)
{
EFI_STATUS eRc;
gBS = SystemTable->BootServices;
gRT = SystemTable->RuntimeServices;
eRc = gBS->LocateProtocol(
&gEfiGraphicsOutputProtocolGuid,
NULL,
(VOID**)&gGOP);
if (EFI_ERROR(eRc) || gGOP == NULL) {
return EFI_SUCCESS;
}
_util_fill_screen(&gWhite);
_util_render_text(0, 0, "HELLO WORLD!");
_util_wait(WAIT_SECONDS);
return EFI_SUCCESS;
}
I tested it on a pc and on a mac it runs on both. Using the tools provided by LVGL on their website you can use any font you want.
If you target MacEFI specifically, you'll need an additional protocol call to force the console into text mode, like this.

IMG_Load: Couldn't open xxx.png

Context: I am currently trying to practice my C skills a little bit with the SDL 2.0.7 and SDL2_image-2.0.2.
Problem: I get an error message during the execution of my program "IMG_Load: Couldn't open xxx.png". The error seems stupid as it is very explicit: "i can't find the image", but as the image is in the appropriate folder... I think I need a fresh eye to spot the stupid mistake.
Platform: Windows 10
IDE: Visual Studio 2017
Steps done to solve the problem:
1) Tried to reduce my code lenght/functionalities to its minimum. Result: Error is still here.
2) I created a new project and copy/pasted the simplified code. Result: On the new project, there is no error, everything is working fine.
3) I compared project's options and the folder. To me they are the same:
It shouldn't be useful but just in case, here is my:
Code sample:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <SDL.h>
#include <SDL_image.h>
#include <SDL_render.h>
#include "SDL_timer.h"
int main(int argc, char *argv[])
{
printf("argc = %d\n", argc);
for (int i = 0; i < argc; ++i)
{
printf("argv[ %d ] = %s\n", i, argv[i]);
}
SDL_Window* pWindow = NULL;
SDL_Renderer* pRenderer = NULL;
SDL_Texture* pTexture = NULL;
SDL_Surface* pLoadedSurface = NULL;
SDL_Rect* tileClipsArray = NULL;
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))
{
fprintf(stderr, "Erreur d'initialisation de la SDL : %s\n", SDL_GetError());
}
//Initialize PNG loading
int imgFlags = IMG_INIT_PNG;
if (!(IMG_Init(imgFlags) & imgFlags))
{
printf("IMG_Load: %s\n", IMG_GetError());
}
pWindow = SDL_CreateWindow("TestLoadingImage",
SDL_WINDOWPOS_CENTERED, // initial X position.
SDL_WINDOWPOS_CENTERED, // Initial Y position.
640, // Width, in pixels.
480, // Height, in pixels.
SDL_WINDOW_OPENGL); // Window flags
assert(NULL != pWindow);
//Create renderer for the window
pRenderer = SDL_CreateRenderer(pWindow,
-1, // Index of the rendering driver to initialize, -1 to initialize the first one supporting the requested flags.
SDL_RENDERER_ACCELERATED
| SDL_RENDERER_PRESENTVSYNC); // RendererFlags
assert(NULL != pRenderer);
//Initialize renderer color
SDL_SetRenderDrawColor(pRenderer, 0xFF, 0xFF, 0xFF, 0xFF);
pLoadedSurface = IMG_Load("GroundTiles.png");
if (NULL == pLoadedSurface)
{
printf("IMG_Load: %s\n", IMG_GetError());
assert(NULL != pLoadedSurface);
}
//Create texture from surface pixels
pTexture = SDL_CreateTextureFromSurface(pRenderer, pLoadedSurface);
assert(NULL != pTexture);
//Get image dimensions
const int textureWidth = pLoadedSurface->w;
const int textureHeight = pLoadedSurface->h;
const int tileClipWidth = 128;
const int tileClipHeight = 128;
const int nbLines = textureHeight / tileClipHeight;
const int nbColumns = textureWidth / tileClipWidth;
const int nbTileClips = nbLines + nbColumns;
tileClipsArray = malloc(nbTileClips * sizeof(SDL_Rect));
int tileClipIndex = 0;
for (int tileClipLineIndex = 0; tileClipLineIndex < nbLines; ++tileClipLineIndex)
{
for (int tileClipColumnIndex = 0; tileClipColumnIndex < nbColumns; ++tileClipColumnIndex)
{
tileClipsArray[tileClipIndex].x = tileClipColumnIndex * tileClipWidth;
tileClipsArray[tileClipIndex].y = tileClipLineIndex * tileClipHeight;
tileClipsArray[tileClipIndex].w = tileClipWidth;
tileClipsArray[tileClipIndex].h = tileClipHeight;
++tileClipIndex;
}
}
//Get rid of old loaded surface
SDL_FreeSurface(pLoadedSurface);
pLoadedSurface = NULL;
int canLoop = 1;
SDL_Event event;
int lastUpdate = SDL_GetTicks();
int now = 0;
int timeToSpendPerClip = 5000;
int timeSpentwithThisClip = 0;
int clipToUse = 0;
while (canLoop)
{
now = SDL_GetTicks();
if (now - lastUpdate > 16)
{
timeSpentwithThisClip += now - lastUpdate;
lastUpdate = now;
// We are processing all the events received this frame.
while (SDL_PollEvent(&event))
{
// We need to know what kind of event we are dealing with.
switch (event.type)
{
case SDL_QUIT:
canLoop = 0;
break;
}
}
SDL_RenderClear(pRenderer);
if (timeSpentwithThisClip > timeToSpendPerClip)
{
clipToUse = rand() % 4;
timeSpentwithThisClip = 0;
}
// Set rendering space and render to screen.
SDL_Rect renderQuad;
renderQuad.x = 50;
renderQuad.y = 50;
renderQuad.w = tileClipsArray[clipToUse].w;
renderQuad.h = tileClipsArray[clipToUse].h;
SDL_RenderCopyEx(pRenderer, pTexture, &tileClipsArray[clipToUse], &renderQuad, 0.0, NULL, SDL_FLIP_NONE);
SDL_RenderPresent(pRenderer);
}
}
SDL_DestroyTexture(pTexture);
free(tileClipsArray);
SDL_DestroyRenderer(pRenderer);
pRenderer = NULL;
SDL_DestroyWindow(pWindow);
pWindow = NULL;
IMG_Quit();
SDL_Quit();
return EXIT_SUCCESS;
}
I'm probably going to copy/paste all my files from the project 1 into the project 2, but I would like to understand my mistake!

how to stop linux framebuffer to clear automatically while drawing multiple frames

I am writing a gif decoder, This image is an animated image.When I write the first frame, it displays fine. When, I display the second frame, it displays only the changed pixels. Other pixels are automatically changed to black. I don't know why?.
My first frame has the complete picture.
The second frame has again only the pixel changed and it contains the rest of the unchanged pixels.
Now, when I draw the second buffer, it redraws the unchanged pixels also. And the unchanged pixels are drawn as black ( or precisely in monitor I see these unchanged pixels are absent). That's when it has to draw the second frame.It draws the changed pixels( which is correct), but it re-draws the unchanged pixel as well. And this unchanged pixel are seen as a black ( that is no color). I feel it is a refreshing issue. Or It could be something else. Help is appreciated.
Required: It should redraw the complete image.
In short, this is the snippet of my function.
Unfortunately, it clears off the previous display - linux framebuffer.
I want to stop clearning the linux framebuffer.
here is the complete file.
/** This is using the Direct Fb calls here; and is tightly coupled with Linux Framebuffer **/
static int fbfd = 0;
static struct fb_var_screeninfo vinfo;
static struct fb_fix_screeninfo finfo;
static long int screensize = 0;
static char *fbp = 0;
static int x = 0, y = 0;
static long int location = 0;
/** This is a clone to linux Frame buffer, and will be called to dump on Framebuffer **/
char *local_display_mem;
/** local functions **/
static void SetBackground(FrameData *tempInfo);
static void SetPixel(char *fbp, unsigned int x, unsigned int y, Byte red, Byte green, Byte blue);
/** This is the entry function to initialize the display **/
void display_init()
{
// Open the file for reading and writing
fbfd = open("/dev/fb0", O_RDWR);
if (fbfd == -1)
{
perror("cannot open framebuffer device");
exit(1);
}
#ifdef DEBUG
printf("The framebuffer device was opened successfully.\n");
#endif
/** Read the Screen Information **/
if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo) == -1)
{
perror("Driver error-- reading fixed information");
exit(1);
}
// Get variable screen information
if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo) == -1)
{
perror("Error reading variable information");
exit(1);
}
#ifdef DEBUG
printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
#endif
// Figure out the size of the screen in bytes
screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
// Map the device to memory
fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
local_display_mem = (char*)malloc(screensize);
if ((int)fbp == -1)
{
perror("Error: mmap failed\r\n");
exit(1);
}
#ifdef DEBUG
printf("The framebuffer device was mapped to memory successfully.\n");
#endif
printf("Shreyas..Display Initialized..\r\n");
//munmap(fbp, screensize);
//close(fbfd);
}
/** This function is called by gif_read to display the Image **/
void Display(FrameData *FrameInfo)
{
short int ImageStartX = 0;
short int ImageStartY = 0;
int Index = 0;
printf("\r\n INFO: Display Called.\r\n");
while(1)
{
Index = 0;
ImageStartX = (FrameInfo->frameScreenInfo.LeftPosition);
ImageStartY = (FrameInfo->frameScreenInfo.TopPosition);
while(ImageStartY < ((FrameInfo->frameScreenInfo.ImageHeight)+(FrameInfo->frameScreenInfo.TopPosition)))
{
while(ImageStartX < ((FrameInfo->frameScreenInfo.ImageWidth)+(FrameInfo->frameScreenInfo.LeftPosition)))
{
if(FrameInfo->frame[Index] != FrameInfo->transperencyindex)
{
SetPixel(local_display_mem,ImageStartX,ImageStartY,((FrameInfo->CMAP)+(FrameInfo->frame[Index]))->Red,((FrameInfo->CMAP)+(FrameInfo->frame[Index]))->Green,((FrameInfo->CMAP)+(FrameInfo->frame[Index]))->Blue);
}
Index++;
ImageStartX++;
}
ImageStartY++;
ImageStartX=(FrameInfo->frameScreenInfo.LeftPosition);
}
printf("INFO:..Dumping Framebuffer\r\n");
memcpy(fbp,local_display_mem,screensize);
/** Tune this multiplication to meet the right output on the display **/
usleep((FrameInfo->InterFrameDelay)*100000);
if( FrameInfo->DisposalMethod == 2)
{
printf("set the Background\r\n");
SetBackground(FrameInfo);
}
FrameInfo = FrameInfo->Next;
}
}
static void SetBackground(FrameData *tempInfo)
{
unsigned int ImageStartX=0;
unsigned int ImageStartY=0;
ImageStartX=(tempInfo->frameScreenInfo.LeftPosition);
ImageStartY=(tempInfo->frameScreenInfo.TopPosition);
while(ImageStartY<(tempInfo->frameScreenInfo.ImageHeight))
{
while(ImageStartX<(tempInfo->frameScreenInfo.ImageWidth))
{
SetPixel(local_display_mem,ImageStartX,ImageStartY,255,255,255);
ImageStartX++;
}
ImageStartX=(tempInfo->frameScreenInfo.LeftPosition);
ImageStartY++;
}
}
static void SetPixel(char *fbp_lc, unsigned int x, unsigned int y, Byte red, Byte green, Byte blue)
{
//printf("Shreyas..set pixel called\r\n");
location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +
(y+vinfo.yoffset) * finfo.line_length;
if (vinfo.bits_per_pixel == 32)
{
*(fbp_lc + location) = blue; // Some blue
*(fbp_lc + location + 1) = green; // A little green
*(fbp_lc + location + 2) = red; // A lot of red
*(fbp_lc + location + 3) = 0; // No transparency
//location += 4;
}
else
{ //assume 16bpp
unsigned short int t = red<<11 | green << 5 | blue;
*((unsigned short int*)(fbp_lc + location)) = t;
}
//printf("Shreyas..set pixel exit called\r\n");
}
/** This is windows version of display function, and it works correctly.
void Display(FrameData *FrameInfo)
{
short int ImageStartX=0;
short int ImageStartY=0;
int Index=0;
DisplayCntrl=GetDC(hWnd);
printf("Shreyas.. Display Init is called\r\n");
//display_init();
while(1)
{
Index=0;
ImageStartX=(FrameInfo->frameScreenInfo.LeftPosition);
ImageStartY=(FrameInfo->frameScreenInfo.TopPosition);
while(ImageStartY<((FrameInfo->frameScreenInfo.ImageHeight)+(FrameInfo->frameScreenInfo.TopPosition)))
{
while(ImageStartX<((FrameInfo->frameScreenInfo.ImageWidth)+(FrameInfo->frameScreenInfo.LeftPosition)))
{
if(FrameInfo->frame[Index]!=FrameInfo->transperencyindex)
SetPixel(DisplayCntrl,ImageStartX,ImageStartY,RGB(((FrameInfo->CMAP)+(FrameInfo->frame[Index]))->Red,((FrameInfo->CMAP)+(FrameInfo->frame[Index]))->Green,((FrameInfo->CMAP)+(FrameInfo->frame[Index]))->Blue));
Index++;
ImageStartX++;
}
ImageStartY++;
ImageStartX=(FrameInfo->frameScreenInfo.LeftPosition);
}
Sleep((FrameInfo->InterFrameDelay*10));
WaitForSingleObject(hWnd,10);
if( FrameInfo->DisposalMethod==2)
{
SETBACKGROUND(FrameInfo);
}
FrameInfo=FrameInfo->Next;
}
}
This is the windows version of the same code.
extern hWnd;
HDC DisplayCntrl;
void SETBACKGROUND(FrameData *tempInfo)
{
unsigned int ImageStartX=0;
unsigned int ImageStartY=0;
ImageStartX=(tempInfo->frameScreenInfo.LeftPosition);
ImageStartY=(tempInfo->frameScreenInfo.TopPosition);
while(ImageStartY<(tempInfo->frameScreenInfo.ImageHeight))
{
while(ImageStartX<(tempInfo->frameScreenInfo.ImageWidth))
{
SetPixel(DisplayCntrl,ImageStartX,ImageStartY,RGB(255,255,255));
ImageStartX++;
}
ImageStartX=(tempInfo->frameScreenInfo.LeftPosition);
ImageStartY++;
}
}
void Display(FrameData *FrameInfo)
{
short int ImageStartX=0;
short int ImageStartY=0;
int Index=0;
DisplayCntrl=GetDC(hWnd);
printf("the size of short int is %d",sizeof(short int));
while(1)
{
Index=0;
ImageStartX=(FrameInfo->frameScreenInfo.LeftPosition);
ImageStartY=(FrameInfo->frameScreenInfo.TopPosition);
while(ImageStartY<((FrameInfo->frameScreenInfo.ImageHeight)+(FrameInfo->frameScreenInfo.TopPosition)))
{
while(ImageStartX<((FrameInfo->frameScreenInfo.ImageWidth)+(FrameInfo->frameScreenInfo.LeftPosition)))
{
if(FrameInfo->frame[Index]!=FrameInfo->transperencyindex)
{
SetPixel(DisplayCntrl,ImageStartX,ImageStartY,RGB(((FrameInfo->CMAP)+(FrameInfo->frame[Index]))->Red,((FrameInfo->CMAP)+(FrameInfo->frame[Index]))->Green,((FrameInfo->CMAP)+(FrameInfo->frame[Index]))->Blue));
}
Index++;
ImageStartX++;
}
ImageStartY++;
ImageStartX=(FrameInfo->frameScreenInfo.LeftPosition);
}
Sleep((FrameInfo->InterFrameDelay*10));
WaitForSingleObject(hWnd,10);
if( FrameInfo->DisposalMethod==2)
{
SETBACKGROUND(FrameInfo);
}
FrameInfo=FrameInfo->Next;
}
}
Since you use a local memory buffer local_display_mem, it doesn't matter if somebody would clear the framebuffer - the memcpy will overwrite every pixel.
This means that the condition FrameInfo->frame[Index] != FrameInfo->transperencyindex is always true for some reason since that would cause the algorithm to set each pixel again instead of only updating the changed pixels.

How to directly query the camera about image luminance/ Skip compentation in OpenCV

I am developing a program in VS 2010 using OpenCV. I want to measure the luminance of every frame that the computer's camera captures. However, the camera's software stabilizes the luminance after 2-3 frames. Eg, if i put my thumb in front of the camera the first frame's luminance is 2 (scale from 0 to 255), but then while keeping my thumb in front of the camera the luminance becomes 7 and the 20 - it is stabilized there for the next frames. So the camera tries to make too dark pictures brighter and too bright pictures darker.
How can i measure the actual luminance without the camera's interference?
My code is:
#ifdef _CH_
#pragma package <opencv>
#endif
#include "stdafx.h"
#include <highgui.h>
#include "cv.h"
#include <stdio.h>
#include <stdlib.h>
#include "..\utilities.h"
int _tmain(int argc, _TCHAR* argv[])
{
FILE *file;
IplImage *img;
IplImage* grayscale_image;
int c, i, j, Luminance = 0, Pixel_Num = 0;
int Avg_Luminance;
int width_step;
int pixel_step;
// allocate memory for an image
// capture from video device #1
CvCapture* capture;
// create a window to display the images
cvNamedWindow("mainWin", CV_WINDOW_AUTOSIZE);
// position the window
cvMoveWindow("mainWin", 5, 5);
while(1)
{
if(file = fopen("luminance_value.txt", "w+"))
{
// retrieve the captured frame
capture= cvCaptureFromCAM(1);
img=cvQueryFrame(capture);
grayscale_image = cvCreateImage( cvGetSize(img), 8, 1 );
cvConvertImage( img, grayscale_image );
width_step= grayscale_image->widthStep;
pixel_step= grayscale_image->widthStep/grayscale_image->width;
Pixel_Num = grayscale_image->width * grayscale_image->height;
for(i = 0; i < grayscale_image->height; i++)
{
for(j = 0; j < grayscale_image->width; j++)
{
unsigned char* point = GETPIXELPTRMACRO( grayscale_image, j, i, width_step, pixel_step);
Luminance += point[0];
}
}
Avg_Luminance = Luminance / Pixel_Num;
//Avg_Luminance = cvGetCaptureProperty(capture,CV_CAP_PROP_BRIGHTNESS);
//file = fopen("luminance_value.txt", "w+");
fprintf(file, "%d", Avg_Luminance);
fclose(file);
printf("Avg_Luminance = %d\n", Avg_Luminance);
Luminance = 0;
Pixel_Num = 0;
// show the image in the window
cvShowImage("mainWin", grayscale_image );
cvReleaseCapture(&capture);
// wait 10 ms for a key to be pressed
c=cvWaitKey(10000);
// escape key terminates program
if(c == 27)
break;
}
else
{
continue;
}
}
return 0;
}

Resources