I'm working on a macOS menubar app that locks any mouse events. What I'm trying to achieve is that after sending mouse events to the CGEventRef based callback, I cannot click anywhere (naturally) but the problem is I cannot quit the loop because of that.
Every time I need to close it, I'm switching to the Xcode app to stop the running app.
This is the main function and the events that I'm sending to the callback function;
void lockTrackpad(void) {
// For keyboard inputs, I add CGEventMaskBit(kCGEventKeyUp)| CGEventMaskBit(kCGEventKeyDown)| CGEventMaskBit(NX_SYSDEFINED)
CGEventMask mask = (
CGEventMaskBit(kCGEventMouseMoved)
| CGEventMaskBit(kCGEventLeftMouseUp)
| CGEventMaskBit(kCGEventLeftMouseDown)
| CGEventMaskBit(kCGEventRightMouseUp)
| CGEventMaskBit(kCGEventRightMouseDown)
| CGEventMaskBit(kCGEventScrollWheel)
);
eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, mask, CGEventCallback_r, nil);
...
}
And this is the callback function;
CGEventRef CGEventCallback_r(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
CGKeyCode keycode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
printf("Event Tap: %d\n", keycode);
if (keycode == 43) {
printf("quit the application \n");
exit(EXIT_SUCCESS);
}
return nil;
}
What I want is, for example when the loop is running, if press "control + U + L", I want to run some function to exit the loop. I believe in order to get the keyboard input, I should send the keyboard events as well but I couldn't figure out how to detect my keyboard shortcuts.
I added the keyboard bits to the mask.
The app is sandboxed.
I want to do something like this;
CGEventRef CGEventCallback_r(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
CGKeyCode keycode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
// When pressing "control + U + L", call a function
if (...) {
printf("pressed control + U + L \n");
doSomething();
}
return nil;
}
I figured out like this;
CGEventRef CGEventCallback_r(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
if (type == kCGEventKeyDown) {
CGKeyCode keyCode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
if (CGEventGetFlags(event) & kCGEventFlagMaskControl) {
// control key is pressed
controlKeyPressed = true;
}
if (keyCode == kVK_ANSI_U) {
// u key is pressed
uKeyPressed = true;
}
if (keyCode == kVK_ANSI_L) {
// l key is pressed
lKeyPressed = true;
}
if (controlKeyPressed && uKeyPressed && lKeyPressed) {
// doSomething();
}
return event;
} else if (type == kCGEventKeyUp) {
CGKeyCode keyCode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
// set the variable to false just like above.
}
if (keycode == 43) {
printf("quit the application \n");
exit(EXIT_SUCCESS);
}
return nil;
}
Also, don't forget to put your variables on a global scope.
I am trying to create a process with two windows. Each window has a different thread for drawing. The problem is that when I run the program, after a random amount of time something goes wrong and gives me a segmentation fault. Here is my code:
main.cpp
std::thread *thr1 = new std::thread(thread_func);
std::thread *thr2 = new std::thread(thread_func);
thread.cpp:
Window *win = new Window(display_name, 1024, 768);
Renderer *rend = win->CreateRenderer();
Texture *pic = new Texture(rend, path);
while (!quit)
{
usleep (10000);
pic->DrawToWindow(src_rect,rect);
rend->SetColor(255,255,255,255);
rend->DrawLine(10,10,300,300,4);
}
delete pic;
delete rend;
delete win;
Window.cpp:
Window::Window(std::string &name, uint32_t w, uint32_t h, uint32_t x, uint32_t y)
: window(nullptr),
windowRect()
{
if (!SDL_WasInit(SDL_INIT_VIDEO))
{
PDEBUG("ERROR: SDL Was not inited please init platform first!\n");
return;
}
//Create Window
window = SDL_CreateWindow(name.c_str(), x, y, w, h, SDL_WINDOW_OPENGL);
if (window == nullptr)
{
PDEBUG("ERROR: SDL_CreateWindow() %s\n", SDL_GetError());
return;
}
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
}
Window::~Window()
{
if (window != nullptr)
{
SDL_DestroyWindow(window);
window = nullptr;
}
}
void
Window::Flush()
{
SDL_GL_SwapWindow(window);
}
Renderer*
Window::CreateRenderer()
{
return new Renderer(this);
}
Renderer.cpp:
Renderer::Renderer(Window *win)
: window(win),
render(nullptr),
context()
{
if (win == nullptr)
{
PDEBUG("ERROR: Window is NULL\n");
return;
}
//Create Renderer
render = SDL_CreateRenderer(win->window, -1, SDL_RENDERER_ACCELERATED);
if (render == nullptr)
{
PDEBUG("ERROR: SDL_CreateRenderer(): %s\n", SDL_GetError());
return;
}
//Create Window OpenGL Context
//context = SDL_GL_CreateContext(win->window);
//if (SDL_GL_MakeCurrent(window->window, context) != 0)
// PDEBUG("ERROR: SDL_GL_MakeCurrent() %s\n", SDL_GetError());
Clear();
}
Renderer::~Renderer()
{
if (render != nullptr)
{
SDL_DestroyRenderer(render);
render = nullptr;
//SDL_GL_DeleteContext(context);
}
}
bool
Renderer::DrawLine(int xStart, int yStart, int xEnd, int yEnd, int width)
{
//if (SDL_GL_MakeCurrent(window->window, context) != 0)
// PDEBUG("ERROR: SDL_GL_MakeCurrent() %s\n", SDL_GetError());
//}
glLineWidth(width);
if (SDL_RenderDrawLine(render, xStart, yStart, xEnd, yEnd) < 0)
{
PDEBUG("ERROR: SDL_RenderDrawLine() %s\n", SDL_GetError());
return false;
}
return true;
}
Do I have to draw in only one thread for two windows, use synchronization to drawing, or use SDL_Thread for threads?
Did i have to draw in only one thread for two windows
Yes.
From the SDL2 threading documentation:
NOTE: You should not expect to be able to create a window, render, or receive events on any thread other than the main one. For platform-specific exceptions or complicated options ask on the mailing list or forum.
Hello I found a solution for 2 windows in different threads.
Here is example:
static bool quit = false;
void
thread_function(SDL_Window* win, SDL_Renderer *rend)
{
SDL_Event event;
while(!quit)
{
//DO THINGS with renderer and window. (Draw, Fill, Present Textures and others)
SDL_PollEvent(&event);
}
}
For Every thread you need to poll event because window events also are in Event Query.
I am trying to write a simple application to record audio on my galaxy watch using native app development in C.
So far I got to the stage when I start the app and have a button that starts and stops the recording.
For some reason I cannot get the recorder to work, it returns rerror -38, which is Internal error, in the logs I can see this:
mm_camcorder_audiorec.c: _mmcamcorder_audio_command(530) > file delete(/opt/usr/home/owner/apps_rw/org.example.audiorecorder/data/AUDIO-2019-11-24_01:15:51.3gp)
But I have no idea why it's deleted on recorder_commit.
What am I missing? Can you help?
Code for the application:
#include <app.h>
#include <stddef.h>
#include <privacy_privilege_manager.h>
// Functions for creating and managing user interface elements. Here are all the widgets,
// such as: windows, buttons, etc.
#include <Elementary.h>
//Functions for using callback for hardware Back button and for
//using extended widgets for a circle screen
#include <efl_extension.h>
#include "audiorecorder.h"
#include <recorder.h>
#include <camera.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
typedef struct _UIData
{
Evas_Object *win;
Evas_Object *box;
Evas_Object *icon;
Evas_Object *button;
} UIData;
#define TEXT_BUF_SIZE 256
#define FILENAME_PREFIX "AUDIO"
#define NUMBER_OF_PRIVILEGES 3
#define START_RECORD_IMAGE_PATH "images/start-recording.jpg"
#define STOP_RECORD_IMAGE_PATH "images/stop-recording.png"
static recorder_h g_recorder;
bool permission_granted = false;
int user_clicked = 0;
static void
_state_changed_cb(recorder_state_e previous,
recorder_state_e current,
bool by_policy, void *user_data)
{
dlog_print(DLOG_INFO, LOG_TAG,
"_recorder_state_changed_cb (prev: %d, curr: %d)\n",
previous,
current);
}
static void
_recorder_recording_limit_reached_cb(
recorder_recording_limit_type_e type,
void *user_data)
{
dlog_print(DLOG_DEBUG, LOG_TAG, "Recording limit reached: %d\n", type);
}
/* Check the audio recorder state */
static bool
_recorder_expect_state(recorder_h recorder,
recorder_state_e expected_state)
{
recorder_state_e state;
int error_code = recorder_get_state(recorder, &state);
dlog_print(DLOG_INFO, LOG_TAG,
"recorder state = %d, expected recorder state = %d, error_code = %d",
state, expected_state, error_code);
if (state == expected_state)
return true;
return false;
}
static void
_win_back_cb(void *data, Evas_Object *obj, void *event_info)
{
UIData *ui = data;
elm_win_lower(ui->win);
}
static void
app_get_resource(const char *edj_file_in, char *edj_path_out, int edj_path_max)
{
char *res_path = app_get_resource_path();
if (res_path)
{
snprintf(edj_path_out, edj_path_max, "%s%s", res_path, edj_file_in);
free(res_path);
}
}
static void
_icon_create(UIData *ui)
{
dlog_print(DLOG_INFO, LOG_TAG, "icon create");
char image_path[PATH_MAX] =
{ 0, };
ui->icon = elm_image_add(ui->button);
elm_image_resizable_set(ui->icon, EINA_TRUE, EINA_TRUE);
app_get_resource(START_RECORD_IMAGE_PATH, image_path, (int) PATH_MAX);
int error_code = elm_image_file_set(ui->icon, image_path, NULL);
dlog_print(DLOG_INFO, LOG_TAG, "res path: %s error_code: %d", image_path, error_code);
evas_object_show(ui->icon);
}
static void
_button_create(UIData *ui)
{
dlog_print(DLOG_INFO, LOG_TAG, "button create");
ui->button = elm_button_add(ui->box);
_icon_create(ui);
elm_object_style_set(ui->button, "circle");
evas_object_size_hint_weight_set(ui->button, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(ui->button, 0.5, 0.4);
evas_object_size_hint_min_set(ui->button, ELM_SCALE_SIZE(90), ELM_SCALE_SIZE(90));
elm_object_content_set(ui->button, ui->icon);
evas_object_show(ui->button);
}
static void
_box_create(UIData *ui)
{
dlog_print(DLOG_INFO, LOG_TAG, "box create");
ui->box = elm_box_add(ui->win);
evas_object_size_hint_weight_set(ui->box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
_button_create(ui);
elm_object_content_set(ui->box, ui->button);
elm_box_pack_end(ui->box, ui->button);
evas_object_show(ui->box);
}
static void
_window_create(UIData *ui)
{
dlog_print(DLOG_INFO, LOG_TAG, "window create");
// Create and configure the main window
ui->win = elm_win_util_standard_add(NULL, NULL);
// Create a conformant - in this case, the main container in the application,
// which is also the interface between the application and
// system elements, such as a keyboard.
_box_create(ui);
// Set the size of the conformant widget the same as the size of the window
elm_win_resize_object_add(ui->win, ui->box);
//Register the function that will be called
//when you press the hardware Back button
eext_object_event_callback_add(ui->win, EEXT_CALLBACK_BACK, _win_back_cb, ui);
// /Always display the window only after the entire base
// user interface will be displayed.
evas_object_show(ui->win);
}
static void permission_request_cb(ppm_call_cause_e cause,
ppm_request_result_e result,
const char *privilege,
void *user_data)
{
dlog_print(DLOG_INFO, LOG_TAG, "callback called for privilege: %s with cause: %d, the result was: %d", privilege,
cause,
result);
user_clicked++;
}
void *func(void *arg)
{
while (user_clicked < NUMBER_OF_PRIVILEGES)
{
dlog_print(DLOG_INFO, LOG_TAG, "waiting for user to accept all privileges, so far accepted: %d", user_clicked);
sleep(1);
}
return 0;
}
static void _button_click_cb(void *data, Evas_Object *button, void *ev)
{
UIData *ui = data;
int error_code = 0;
dlog_print(DLOG_INFO, LOG_TAG, "clicked button!");
char image_path[PATH_MAX] = { 0, };
const char *image;
const char *group;
char *path = NULL;
recorder_state_e state;
elm_image_file_get(ui->icon, &image, &group);
dlog_print(DLOG_INFO, LOG_TAG, "file get, image: %s, group: %s", image, group);
if (strstr(image, "start") != NULL) {
app_get_resource(STOP_RECORD_IMAGE_PATH, image_path, (int) PATH_MAX);
error_code = elm_image_file_set(ui->icon, image_path, NULL);
dlog_print(DLOG_INFO, LOG_TAG, "setting icon to stop, error code: %d", error_code);
dlog_print(DLOG_INFO, LOG_TAG, "starting recorder");
if (_recorder_expect_state(g_recorder, RECORDER_STATE_READY)
|| _recorder_expect_state(g_recorder, RECORDER_STATE_PAUSED))
{
error_code = recorder_start(g_recorder);
dlog_print(DLOG_INFO, LOG_TAG, "starting recorder result: %d", error_code);
}
else
{
recorder_get_state(g_recorder, &state);
dlog_print(DLOG_INFO, LOG_TAG, "Failure, recorder in wrong state = %d", state);
}
}
else if(strstr(image, "stop") != NULL)
{
app_get_resource(START_RECORD_IMAGE_PATH, image_path, (int) PATH_MAX);
error_code = elm_image_file_set(ui->icon, image_path, NULL);
dlog_print(DLOG_INFO, LOG_TAG, "setting icon to start, error code: %d", error_code);
/* Stop the recorder and save the recorded data to a file */
if (_recorder_expect_state(g_recorder, RECORDER_STATE_RECORDING)
|| _recorder_expect_state(g_recorder, RECORDER_STATE_PAUSED))
{
error_code = recorder_get_filename(g_recorder, &path);
dlog_print(DLOG_INFO, LOG_TAG, "going to save audio to file %s error code: %d", path, error_code);
error_code = recorder_commit(g_recorder);
if (error_code != RECORDER_ERROR_NONE)
{
dlog_print(DLOG_INFO, LOG_TAG, "Failure, error code = %d", error_code);
recorder_cancel(g_recorder);
}
}
else
{
recorder_get_state(g_recorder, &state);
dlog_print(DLOG_INFO, LOG_TAG, "Failure, recorder in wrong state = %d", state);
}
}
}
static bool
_app_create_cb(void *data)
{
UIData *ui = data;
_window_create(ui);
evas_object_smart_callback_add(ui->button, "clicked", _button_click_cb, ui);
return true;
}
static void
_app_control_cb(app_control_h app_control, void *data)
{
/* Handle the launch request. */
}
static void
_app_pause_cb(void *data)
{
/* Take necessary actions when the application becomes invisible. */
}
static void
_app_resume_cb(void *data)
{
ppm_check_result_e privilege_results_array[NUMBER_OF_PRIVILEGES];
const char *privilege_array[NUMBER_OF_PRIVILEGES];
pthread_t pid;
char *recorder_privilege = "http://tizen.org/privilege/recorder";
char *media_storage_privilege = "http://tizen.org/privilege/mediastorage";
char *ex_media_storage_privilege = "http://tizen.org/privilege/externalstorage";
privilege_array[0] = malloc(strlen(recorder_privilege) + 1);
privilege_array[1] = malloc(strlen(media_storage_privilege) + 1);
privilege_array[2] = malloc(strlen(ex_media_storage_privilege) + 1);
strcpy((char*) privilege_array[0], recorder_privilege);
strcpy((char*) privilege_array[1], media_storage_privilege);
strcpy((char*) privilege_array[2], ex_media_storage_privilege);
int result = ppm_check_permissions(privilege_array, NUMBER_OF_PRIVILEGES, privilege_results_array);
dlog_print(DLOG_INFO, LOG_TAG, "checking permission for recorder. Result: %d", result);
for (int i = 0; i < NUMBER_OF_PRIVILEGES; i++)
{
switch (privilege_results_array[i])
{
case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ALLOW:
dlog_print(DLOG_INFO, LOG_TAG, "Privilege: %s, Allowed!", privilege_array[i]);
break;
case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_DENY:
dlog_print(DLOG_INFO, LOG_TAG, "Privilege: %s, Denied!", privilege_array[i]);
break;
case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ASK:
dlog_print(DLOG_INFO, LOG_TAG, "Privilege: %s, Gotta ask for this privilege!", privilege_array[i]);
ppm_request_permission(privilege_array[i], permission_request_cb, NULL);
break;
}
}
pthread_create(&pid, NULL, func, NULL);
}
static void _app_terminate_cb(void *data)
{
UIData *ui = data;
//Before closing the application, delete the main widget (window),
// then all "children" widgets of this window (all widgets of this application) will be deleted.
evas_object_del(ui->win);
/* Release all resources. */
int error_code = recorder_unset_recording_limit_reached_cb(g_recorder);
error_code = recorder_unprepare(g_recorder);
error_code = recorder_unset_state_changed_cb(g_recorder);
error_code = recorder_destroy(g_recorder);
}
int main(int argc, char *argv[])
{
char filename[256] =
{ '\0' };
struct tm localtime =
{ 0 };
time_t rawtime = time(NULL);
int ret = 0;
size_t size = 0;
dlog_print(DLOG_INFO, LOG_TAG, "main");
UIData ui =
{ 0, };
ui_app_lifecycle_callback_s lifecycle_callbacks =
{ 0, };
/* Create the audio recorder handle */
int error_code = recorder_create_audiorecorder(&g_recorder);
if (error_code == RECORDER_ERROR_NONE)
dlog_print(DLOG_INFO, LOG_TAG, "successfully created audiorecorder error code = %d", error_code);
else
dlog_print(DLOG_ERROR, LOG_TAG, "Failure creating audiorecorder error code = %d", error_code);
error_code = recorder_set_state_changed_cb(g_recorder, _state_changed_cb, NULL);
dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_set_state_changed_cb, error code = %d", error_code);
/* Set the audio encoder */
error_code = recorder_set_audio_encoder(g_recorder, RECORDER_AUDIO_CODEC_AAC);
dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_set_audio_encoder, error code = %d", error_code);
/* Set video encoder */
error_code = recorder_set_video_encoder(g_recorder, RECORDER_VIDEO_CODEC_MPEG4);
dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_set_video_encoder, error code = %d", error_code);
/* Set the maximum file size to unlimited 10240 (kB) - 10 mb */
error_code = recorder_attr_set_size_limit(g_recorder, 10240);
dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_attr_set_size_limit, error code = %d", error_code);
/* Set the audio encoder bitrate */
error_code = recorder_attr_set_audio_encoder_bitrate(g_recorder, 28800);
dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_attr_set_audio_encoder_bitrate, error code = %d", error_code);
/* Set the audio device to microphone */
error_code = recorder_attr_set_audio_device(g_recorder, RECORDER_AUDIO_DEVICE_MIC);
dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_attr_set_audio_device, error code = %d", error_code);
/* Set the audio sample rate */
error_code = recorder_attr_set_audio_samplerate(g_recorder, 44100);
dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_attr_set_audio_samplerate, error code = %d", error_code);
/* Set the file format according to the audio encoder */
error_code = recorder_set_file_format(g_recorder, RECORDER_FILE_FORMAT_3GP);
dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_set_file_format, error code = %d", error_code);
/* Create the file name */
dlog_print(DLOG_INFO, LOG_TAG, "data path = %s", app_get_data_path());
if (localtime_r(&rawtime, &localtime) != NULL)
{
size = snprintf(filename, sizeof(filename),
"%s%s-%04i-%02i-%02i_%02i:%02i:%02i.3gp",
app_get_data_path(),
FILENAME_PREFIX,
localtime.tm_year + 1900, localtime.tm_mon + 1,
localtime.tm_mday,
localtime.tm_hour, localtime.tm_min,
localtime.tm_sec);
}
else
{
dlog_print(DLOG_INFO, LOG_TAG, "failed to save audio in a file");
}
/* Set the full path and file name */
/* Set the file name according to the file format */
error_code = recorder_set_filename(g_recorder, filename);
dlog_print(DLOG_INFO, LOG_TAG, "setting file name to: %s, error code = %d",
filename,
error_code);
error_code = recorder_set_recording_limit_reached_cb(g_recorder,
_recorder_recording_limit_reached_cb,
NULL);
dlog_print(DLOG_INFO, LOG_TAG, "setting limit reached callback, error code = %d", error_code);
dlog_print(DLOG_INFO, LOG_TAG, "preparing recorder");
error_code = recorder_prepare(g_recorder);
dlog_print(DLOG_INFO, LOG_TAG, "preparing recorder result: %d", error_code);
lifecycle_callbacks.create = _app_create_cb;
lifecycle_callbacks.terminate = _app_terminate_cb;
lifecycle_callbacks.pause = _app_pause_cb;
lifecycle_callbacks.resume = _app_resume_cb;
lifecycle_callbacks.app_control = _app_control_cb;
ret = ui_app_main(argc, argv, &lifecycle_callbacks, &ui);
if (ret != APP_ERROR_NONE)
{
dlog_print(DLOG_ERROR, LOG_TAG, "watch_app_main() is failed. err = %d",
ret);
}
return ret;
}
In your code, recorder_cancel is called if recorder_commit returns error.
error_code = recorder_commit(g_recorder);
if (error_code != RECORDER_ERROR_NONE)
{
dlog_print(DLOG_INFO, LOG_TAG, "Failure, error code = %d", error_code);
recorder_cancel(g_recorder);
}
So, we need to check why recorder_commit is failed.
Could you show all log since recorder_prepare is called especially with MM_CAMCORDER and GST_LOG log tags?
Note: You can get runtime error with recorder_set_error_cb(). It could help you to debug error.
Thank you for the log.
I have checked it and found that the recorder_commit() was failed because the recorded file size is zero.(I can assume it with time interval of recorder_commit() function call.)
According to log, the path for recording is "/opt/usr/home/owner/apps_rw/org.example.audiorecorder/data/" which is got by app_get_data_path().
But in general, media files are not placed there. So, I suggest to change the recording file path.
Please try it with another path which can be got by storage_foreach_device_supported().
(https://developer.tizen.org/development/api-references/native-application?redirect=https://developer.tizen.org/dev-guide/4.0.0/org.tizen.native.wearable.apireference/group__CAPI__SYSTEM__STORAGE__MODULE.html#ga8f5d871bbc2d8942fe6b725c8f8fc779)
And you can check recording status with recorder_set_recording_status_cb().
If it's not called repeatably, we can assume some problem is occurred.
Otherwise, we can assume it works well.
If recording status callback is not called, it means recorded file will not be created and there is a problem in audiosrc or encoding part of recorder framework.
Could you send me the your test tpk file?(jm80.yang#outlook.com)
I have tested my recording testsuite with same settings(samplerate:16000, bitrate:28800, audio codec:AAC, file format:3GP) on galaxy watch, but it works fine.
So I need to check the issue with test tpk which you made.
I tried to write a small event-based application in C for serial port reading (sources below). My program is to use the WinApi functions. The comport.c has the functions written to handle COM-port (open, read, write), the utils.c has some helper functions.
My program produces always the following output:
COM1 is selected to be listened.
GetCommMask result: 0x00000029 (EV_RXCHAR: 0x0001, EV_CTS: 0x0008, EV_RLSD: 0x0020)
Press any key to proceed...
I/O is pending (WaitCommEvent)...
I/O is pending (WaitCommEvent)...
I/O is pending (WaitCommEvent)...
I/O is pending (WaitCommEvent)...
I/O is pending (WaitCommEvent)...
It seems, that the function WaitCommEvent fails, the GetLastError() gives back error 87 (I/O pending).
Please help me to find out what the problem is, which parameter is invalid? See below the code.
The main.c:
#include "stdafx.h"
#include "comport.h"
#include "utils.h"
#include <conio.h>
#define READ_BUFF_MAX_LENGTH 1024
int _tmain(int argc, _TCHAR* argv[])
{
DWORD dwEvtMask;
HANDLE hComPort;
OVERLAPPED oEventHandler;
int portNum;
DWORD readTotal;
BOOL bOverlappedPending = FALSE;
char readBuff[READ_BUFF_MAX_LENGTH];
if(2 > argc)
{
printf("Use the program: ZliFuerZlvbusInterface.exe XXX (where XXX is the number of com port), \r\ne.G. for COM1 -> ZliFuerZlvbusInterface.exe 1\r\n");
return 1;
}
else
{
portNum = atoi(argv[1]);
if(0 == IsValidComNumber(portNum))
{
printf("ERROR: COM port number %d is invalid (parsed from '%s').\r\n", portNum, argv[1]);
return 2;
}
else
{
printf("COM%d is selected to be listened.\r\n", portNum);
}
}
if(0 == CreateSerialConnection(&hComPort, &oEventHandler, portNum, 1200, 8, EVEN, STOP1))
{
return 3;
}
if(FALSE == GetCommMask(hComPort, &dwEvtMask))
{
printf("GetCommMask failed with error:\r\n");
PrintLastErrorText();
return 4;
}
else
{
printf("GetCommMask result: 0x%08X (EV_RXCHAR: 0x0001, EV_CTS: 0x0008, EV_RLSD: 0x0020)\r\n", dwEvtMask);
}
printf("Press any key to proceed...\r\n");
getch();
while(1)
{
if(0 != kbhit())
{
if(27 == getch()) // ESC pressed
{
printf("Key ESC pressed, exiting...\r\n");
break;
}
}
bOverlappedPending = FALSE;
readTotal = 0;
if(TRUE == WaitCommEvent(hComPort, &dwEvtMask, &oEventHandler))
{
if(dwEvtMask & EV_CTS) // Clear-to-send signal present
{
PrintCurrentDateTime();
printf("COM%d: Clear-to-send signal set\r\n", portNum);
}
if(dwEvtMask & EV_RLSD) // Data-carrier-detect signal present
{
PrintCurrentDateTime();
printf("COM%d: Data-carrier-detect signal set\r\n", portNum);
}
if(dwEvtMask & EV_RXCHAR) // Data received
{
ReadSerial(&hComPort, &oEventHandler, portNum, readBuff, READ_BUFF_MAX_LENGTH);
}
}
else
{
if(ERROR_IO_PENDING == GetLastError())
{
printf("I/O is pending (WaitCommEvent)...\r\n");
bOverlappedPending = TRUE;
}
else
{
printf("WaitCommEvent failed with error:\r\n");
PrintLastErrorText();
}
}
if(TRUE == bOverlappedPending)
{
if(FALSE == GetOverlappedResult(hComPort, &oEventHandler, &readTotal, TRUE))
{
printf("GetOverlappedResult failed with error:\r\n");
PrintLastErrorText();
}
}
}
CloseSerialConnection(&hComPort);
return 0;
}
The comport.c:
#include <stdio.h>
#include "comport.h"
#include "utils.h"
int IsValidComNumber(int com)
{
if ((com < 1) ||
(com > 256))
{
return 0;
}
return 1;
}
int IsValidBaud(int baud)
{
switch(baud)
{
case CBR_110:
case CBR_300:
case CBR_600:
case CBR_1200:
case CBR_2400:
case CBR_4800:
case CBR_9600:
case CBR_14400:
case CBR_19200:
case CBR_38400:
case CBR_56000:
case CBR_57600:
case CBR_115200:
case CBR_128000:
case CBR_256000:
{
return 1;
break;
}
default:
{
break;
}
}
return 0;
}
int IsValidBits(int bits)
{
if ((bits < 5) ||
(bits > 8))
{
return 0;
}
else
{
return 1;
}
}
int CreateSerialConnection(HANDLE* handle, OVERLAPPED* overlapped, int portNumber, int baud, int bits, enum ParType parity, enum StopType stopbits)
{
DCB dcb;
COMMTIMEOUTS timeouts;
TCHAR portVirtualFile[32];
// For serial port name this format must be used (as virtual file): "\\\\.\\COMx"
memset(portVirtualFile, 0, 32);
#ifdef _UNICODE
swprintf(portVirtualFile, 32, L"\\\\.\\COM%d", portNumber);
#else
sprintf_s(portVirtualFile, 32, "\\\\.\\COM%d", portNumber);
#endif
if(0 == IsValidBaud(baud))
{
printf("ERROR: Specified baud rate %d is invalid for serial connection to COM%d.\r\n", baud, portNumber);
return 0;
}
if(0 == IsValidBits(bits))
{
printf("ERROR: Specified number of data bits %d is invalid for serial connection to COM%d.\r\n", bits, portNumber);
return 0;
}
*handle = CreateFile(portVirtualFile, // Specify port device
GENERIC_READ | GENERIC_WRITE, // Specify mode that open device.
0, // the devide isn't shared.
NULL, // the object gets a default security.
OPEN_EXISTING, // Specify which action to take on file.
FILE_FLAG_OVERLAPPED, // Use overlapped I/O.
NULL); // default.
if(*handle == INVALID_HANDLE_VALUE)
{
printf("ERROR: Opening serial port COM%d failed\r\n", portNumber);
return 0;
}
if(FALSE == GetCommState(*handle, &dcb))
{
printf("ERROR: Getting current state of COM%d\r\n", portNumber);
PrintLastErrorText();
return 0;
}
memset(&dcb, 0, sizeof(dcb)); //zero initialize the structure
dcb.DCBlength = sizeof(dcb); //fill in length
dcb.BaudRate = baud; // baud rate
dcb.ByteSize = bits; // data size, xmit and rcv
dcb.Parity = parity; // parity bit
dcb.StopBits = stopbits; // stop bits
if(FALSE == SetCommState(*handle, &dcb))
{
printf("ERROR: Setting new state of COM%d failed.\r\n", portNumber);
PrintLastErrorText();
return 0;
}
if(FALSE == SetCommMask(*handle, EV_RXCHAR | EV_CTS | EV_RLSD))
{
printf("ERROR: Setting new COM MASK (events) for COM%d failed.\r\n", portNumber);
PrintLastErrorText();
return 0;
}
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = 0;
timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 0;
if(FALSE == SetCommTimeouts(*handle, &timeouts))
{
printf("ERROR: Setting timeout parameters for COM%d failed.\r\n", portNumber);
PrintLastErrorText();
return 0;
}
(*overlapped).hEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual-reset event
FALSE, // not signaled
NULL // no name
);
if(NULL == overlapped->hEvent)
{
printf("ERROR: CreateEvent for COM%d failed.\r\n", portNumber);
PrintLastErrorText();
return 0;
}
// Initialize the rest of the OVERLAPPED structure to zero.
overlapped->Internal = 0;
overlapped->InternalHigh = 0;
overlapped->Offset = 0;
overlapped->OffsetHigh = 0;
return 1;
}
int CloseSerialConnection(HANDLE* handle)
{
if(FALSE == CloseHandle(*handle))
{
printf("ERROR: Cannot close handle 0x8.8%X\r\n", *handle);
PrintLastErrorText();
return 0;
}
*handle = NULL;
return 1;
}
int ReadSerial(HANDLE* handle, LPOVERLAPPED ov, int num, char* buffer, int max_len)
{
DWORD readTotal = 0;
if(FALSE == ClearCommError(*handle, NULL, NULL))
{
printf("ClearCommError failed with error:\r\n");
PrintLastErrorText();
return 0;
}
else
{
memset(buffer, 0, max_len);
if(FALSE == ReadFile(*handle, buffer, max_len, &readTotal, ov))
{
printf("ERROR: Reading from port COM%d failed\r\n", num);
if(ERROR_IO_PENDING == GetLastError())
{
printf("I/O is pending (ReadFile)...\r\n");
if(FALSE == GetOverlappedResult(*handle, ov, &readTotal, TRUE))
{
printf("GetOverlappedResult failed with error:\r\n");
PrintLastErrorText();
return 0;
}
}
else
{
PrintLastErrorText();
return 0;
}
}
else
{
PrintCurrentDateTime();
printf("Received %d characters on port COM%d: ", readTotal, num);
PrintBufferContent(buffer, readTotal);
}
}
return 1;
}
The utils.c:
#include <Windows.h>
#include <stdio.h>
#include "utils.h"
void PrintLastErrorText(void)
{
DWORD retSize;
LPTSTR pTemp = NULL;
retSize = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, NULL, GetLastError(), LANG_NEUTRAL, (LPTSTR)&pTemp, 0, NULL);
if ((retSize > 0) &&
(pTemp != NULL))
{
printf("Last error: %s (0x%08X)\r\n", pTemp, GetLastError());
LocalFree((HLOCAL)pTemp);
}
}
void PrintCurrentDateTime(void)
{
SYSTEMTIME systime;
GetLocalTime(&systime);
printf("%04d.%02d.%02d %02d:%02d:%02d:%03d ", systime.wYear, systime.wMonth, systime.wDay, systime.wHour, systime.wMinute, systime.wSecond, systime.wMilliseconds);
}
void PrintBufferContent(char* buff, int len)
{
int i;
for(i = 0; i<len; i++)
{
printf("%02X ", buff[i]);
}
}
You're using the same OVERLAPPED structure for WaitCommEvent and ReadFile. Try using separate/independent OVERLAPPED for each.
UPDATE: Ignore that previous answer.
If your call to WaitCommEvent returns ERROR_IO_PENDING, you're not waiting for it to complete. Rather than loop around and call WaitCommEvent again, you need to wait for the operation to complete (typically via GetOverlappedResult).
You cannot have multiple pending asynchronous requests share a single OVERLAPPED structure. By looping and calling WaitCommEvent again after ERROR_IO_PENDING, that's exactly what's happening.