I create an GtkFixed object and put it into GtkEventBox via gtk_container_add (with destroying previous child, of course). This way works fine on Windows, but on Mac it crashes.
Stack trace showed me that app crashed on gtk_get_parent. I've tried to user set_parent procedure, but it crashes too:
[debug][New Thread 0x1b0f of process 88699]
[debug]Program received signal SIGSEGV, Segmentation fault.
[debug]0x0000000100b3cf3d in gtk_widget_set_parent () from /usr/local/lib/libgtk-3.0.dylib
[debug]>>>>>>cb_gdb:
Program received signal SIGSEGV, Segmentation fault.
In gtk_widget_set_parent () (/usr/local/lib/libgtk-3.0.dylib)
[debug]> bt 30
[debug]#0 0x0000000100b3cf3d in gtk_widget_set_parent () from /usr/local/lib/libgtk-3.0.dylib
Code runned in main thread and in "realize" callback of Window. Same.
The code is:
void main_view_reset_list_view(GtkWidget* list_view, GtkWidget* new_item)
{
GtkEventBox* eb = GTK_EVENT_BOX(list_view);
GtkBin* bin = GTK_BIN(list_view);
GtkContainer* container = GTK_CONTAINER(list_view);
GtkWidget* subview;
subview = gtk_bin_get_child(bin);
if (subview)
{
gtk_container_remove(container, subview);
gtk_widget_unparent (subview);
gtk_widget_destroy(subview);
}
gtk_widget_set_parent(new_item, container); // crash
GtkWidget* parent = gtk_widget_get_parent(container);
GtkWidget* parent2 = gtk_widget_get_parent(new_item); // 0 on windows, crash on mac
if (new_item)
{
gtk_container_add(container, new_item);
gtk_widget_show(new_item);
}
gtk_widget_show(list_view);
}
UPD:
set_parent makes controls dissapeared somehow on windows
I've traced gtk and glib source code and figured that G_IS_OBJECT() fails, which is quite weird. This post have brought me to mind that this issue is pointer-related. I've debugged my code and saw that it is true.
GtkWidget* create_widget()
{
return gtk_fixed_new(); // result points to 0x1018bf830
}
void refresh_widgets()
{
GtkWidget* w = create_widget(); // w points to 0x18bf830
}
I've actually faced with this kind of issue before, so all code which return pointers has been changed:
char* generate_string();
to
void generate_string(char** out);
So in order to fix my problem I have to provide correct pointers in this way. Like:
void create_widget(GtkWidget** out);
UPD: I've noticed difference between pointers. Shortly say, somehow they are trimmed to 32 bit on 64 bit system on return.
UPD2: Finally, I've made all changes and it works that way.
Related
So I am trying to put together a simple fullscreen OpenGL application using CGL and IOHIDManager in order to learn the lower-level APIs. Currently, I am creating an OpenGL context and starting it fullscreen. I am trying to now add keyboard input so I can quit the app. I've found many similar examples of using IOHIDManager to read keys, but no matter what I do my callback does not fire.
My callback is just a function that prints "here". I'm not sure where I am going wrong -- I've tried both CFRunLoopGetCurrent() and CFRunLoopMain(). My main is simply a while loop. What gives?
CFMutableDictionaryRef CreateMatchingDictionary(UInt32 usage_page, UInt32 usage) {
CFMutableDictionaryRef dictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFNumberRef page_number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage_page);
CFDictionarySetValue(dictionary, CFSTR(kIOHIDDeviceUsagePageKey), page_number);
CFRelease(page_number);
CFNumberRef usage_number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
CFDictionarySetValue(dictionary, CFSTR(kIOHIDDeviceUsageKey), usage_number);
CFRelease(usage_number);
return dictionary;
}
void CreateInputManager() {
IOHIDManagerRef hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
CFMutableDictionaryRef matching_dictionary = CreateMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard);
IOHIDManagerSetDeviceMatching(hid_manager, matching_dictionary);
IOHIDManagerRegisterInputValueCallback(hid_manager, KeyboardCallback, NULL);
IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone);
IOHIDManagerScheduleWithRunLoop(hid_manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
}
void KeyboardCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value) {
puts("CALLBACK!");
}
int main() {
// Commented out CGL context & fullscreen window creation
CreateInputManager();
while(true) {
;
}
}
UPDATE
If I put CFRunLoopRun() at the end of function CreateInputManager, my callback is called but the function never returns. How is this supposed to work in a single-threaded CGL app? Is it a strict requirement that IOHIDManager requires a run loop to function?
IOKit and HID work via Mach messaging, which in turn is deeply integrated with the runloop mechanism, as you've found. If you really do want to busy-poll, you can use the CFRunLoopRunInMode function with a zero timeout to check for events.
You may wish to consider using a CVDisplayLink to invoke your rendering code on every vertical frame refresh instead. The display link's callback will be called from the runloop, so you can leave your main thread running in CFRunLoopRun().
See https://developer.apple.com/library/mac/qa/qa1385/_index.html for how Apple recommends you structure event handling in OpenGL applications.
Turns out I need to create a separate pthread with the CreateInputManager function, specify that the IOHIDManager is to schedule the callback on CFRunLoopGetCurrent() and kick off a run loop on that thread by calling CFRunLoopRun().
I wonder if there is a way to get IOHIDManager to work with plain-old polling instead of these callbacks...
Anybody got this problem, anyway I didn't find an answer. The code is simple:
void CbDlg::OnBnClickedOk()
{
for(int i=0; i<1000; i++)
{
HRSRC hRes = ::FindResource(NULL, MAKEINTRESOURCE(IDR_MAINFRAME), RT_GROUP_ICON);
HGLOBAL hResLoad = ::LoadResource(NULL, hRes);
BYTE* pIconBytes = (BYTE*)::LockResource(hResLoad);
int nId = ::LookupIconIdFromDirectory(pIconBytes, TRUE);
hRes = ::FindResource(NULL, MAKEINTRESOURCE(nId), RT_ICON);
DWORD read = ::SizeofResource(NULL ,hRes);
hResLoad = ::LoadResource(NULL, hRes);
pIconBytes = (BYTE*)::LockResource(hResLoad);
if(pIconBytes != NULL)
{
HICON hIcon = ::CreateIconFromResource(pIconBytes, read, TRUE, 0x00030000);
DWORD e = ::GetLastError();
if(hIcon != NULL)
{
::DestroyIcon(hIcon);
}
}
}
}
If I click the Ok button four times (On my computer), CreateIconFromResource start to return NULL (It worked fine before and I could even draw out the icon). As to the GetLastError, it's always return 6 whatever CreateIconFromResource return NULL or not.
When this problem happened, if I drag the title bar to move, UI crashed, see the pictrue.
Of course you can understand this piece of code is just a demo, my real business need to call CreateIconFromResource thousands of times just like this.
UPDATE:
According to Hans' suggestion, I keep tracking the Handles/USER Objects/GDI objects, and found that USER Objects grows 1000 and GDI objects grows 2000 against each clicking to OK button (handles didn't grow), and GDI objects is 9999 when problem happens. But how to release them correctly, when I finish to use? I didn't use that much at one time, but need to load, release, load again, release again... Just like this demo. As MSDN document, I called DestroyIcon for every HICON. What else do I need to do, to finally release the USER/GDI objects?
I found the answer. The success or failure is all due to MSDN.
It says:
"The CreateIconFromResource function calls CreateIconFromResourceEx passing LR_DEFAULTSIZE|LR_SHARED as flags" AND "Do not use this function(DestroyIcon) to destroy a shared icon"
But It also says:
"When you are finished using the icon, destroy it using the DestroyIcon function" in CreateIconFromResource's document.
Actually, the second statement is WRONG.
So, the solution is, using CreateIconFromResourceEx without LR_SHARED, and DestroyIcon every HICON after using.
I try to monitor file changes on OSX 10.10, starting with a fresh Cocoa application in Xcode, just adding the following code.
If I uncomment the last line in the snippet then I receive the file change events perfectly fine. But I can not make this last call because it should be a Cocoa GUI application.
I digged through a lot of documentation and can't find my error. Do I have to initialize or start this whole dispatch subsystem somehow?
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
int fd = open("<FILENAME>", O_EVTONLY);
if (fd == -1) return;
dispatch_queue_t qu = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
if (!qu) {
printf("can not get queue");
return;
}
unsigned long mask =
DISPATCH_VNODE_DELETE |
DISPATCH_VNODE_WRITE |
DISPATCH_VNODE_EXTEND |
DISPATCH_VNODE_ATTRIB |
DISPATCH_VNODE_LINK |
DISPATCH_VNODE_RENAME |
DISPATCH_VNODE_REVOKE;
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, mask, qu);
printf("source created\n");
if (!source) {
close(fd);
return;
}
printf("source valid\n");
dispatch_source_set_event_handler(source, ^{
printf("FILE CHANGED\n");
});
dispatch_resume(source);
printf("source resumed\n");
// If I call dispatch_main() I will receive the file system events as expected.
// But as a Cocoa application, I must not call this.
// Instead, I was under the impression that NSApplicationMain handles this.
//dispatch_main();
}
Grand Central Dispatch objects, such as dispatch sources, are automatically retained and released by ARC in recent versions of the compiler and frameworks.
At the end of your method, the last strong reference to source is lost and ARC is issuing an automatic dispatch_release(source). (It would also release the queue, but the source has another strong reference to that. So, if the source survived, so would the queue.)
You need to keep a strong reference to the source in an instance variable.
I'm debugging Qt5.3.1 on Mac, because my program freezes sometimes (intermittent ). I discovered that it is because the QTimer can't work properly.
In Qt code, they use the following two lines to trigger function activateTimersSourceCallback
CFRunLoopSourceSignal(d->activateTimersSourceRef);
CFRunLoopWakeUp(mainRunLoop());
void QCocoaEventDispatcherPrivate::activateTimersSourceCallback(void *info)
{
static int counter = 0;
NSLog(#"finished activeteTimersSourceCallback %d", counter++);
}
but sometimes, these two lines doesn't work, activateTimersSourceCallback won't get called.
I googled, but I couldn't find any solution? is this a known OS bug?
the initialization details:
// keep our sources running when modal loops are running
CFRunLoopAddCommonMode(mainRunLoop(), (CFStringRef) NSModalPanelRunLoopMode);
CFRunLoopSourceContext context;
bzero(&context, sizeof(CFRunLoopSourceContext));
context.info = d;
context.equal = runLoopSourceEqualCallback;
// source used to activate timers
context.perform = QCocoaEventDispatcherPrivate::activateTimersSourceCallback;
d->activateTimersSourceRef = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
Q_ASSERT(d->activateTimersSourceRef);
CFRunLoopAddSource(mainRunLoop(), d->activateTimersSourceRef, kCFRunLoopCommonModes);
Such behavior very likely can occur when UI event loop is overloaded with events or some business logic takes too long time. You should to check your business logic and move it to separate thread or run asynchronous.
I'm having an issue with some MFC printing code lately. To describe the issue at a high level, we're getting problems when we try to print the same document twice. (Really, it doesn't have to be the SAME document. It seems that it happens when two documents are printed and the child window isn't destroyed and created between them.) The first time, the document prints fine. The second time, the program crashes with an uncaught exception.
This also only started happening after a recent upgrade. This is code that I've literally never touched. It worked fine in version 5 when we were using VS2005 and MFC8. But then, when we upgraded to VS2008 and MFC9, this started happening.
Here's the problem code:
void CReportWnd::OnPrint()
{
CDC dc;
CPrintDialog dlg (FALSE);
CPrintDialog defaults(FALSE);
DEVMODE *ldev_printinfo;
int li_first = 0;
int li_last;
int ret = defaults.GetDefaults();
ldev_printinfo = defaults.GetDevMode();
//ldev_printinfo->dmOrientation = DMORIENT_LANDSCAPE;
dc.Attach (defaults.GetPrinterDC ());
dc.ResetDC(ldev_printinfo);
PROPrint(1, NULL, &dc, NULL, &li_last, true);
dlg.m_pd.hDevMode = ldev_printinfo;
dlg.m_pd.Flags &= ~PD_NOPAGENUMS;
dlg.m_pd.nMinPage = 1;
dlg.m_pd.nFromPage = 1;
dlg.m_pd.nMaxPage = li_last;
dlg.m_pd.nToPage = li_last;
if (dlg.DoModal () == IDOK) {
dc.DeleteDC();
dc.Detach();
dc.Attach (dlg.GetPrinterDC ());
} else {
return;
}
//Set up document info (need to set the name)
DOCINFO di;
::ZeroMemory (&di, sizeof (DOCINFO));
di.cbSize = sizeof (DOCINFO);
di.lpszDocName = "Report";
if(dc.StartDoc(&di) <= 0) {
return;
}
if(dlg.PrintRange()) {
li_first = dlg.m_pd.nFromPage - 1;
li_last = dlg.m_pd.nToPage - 1;
}
//Now do the actual print job to the printing device
PROPrint(1, NULL, &dc, &li_first, &li_last, false);
}
On the int ret = ... line near the top is where the exception is thrown. GetDefaults() throws an access violation exception. But again, only the second time that this function is called. It seems to me like it's some sort of resource issue. Like a resource isn't being freed and allocated properly. But I'm so inexperienced with printing that it could be anything.
If anybody could offer any kind of help, I would really appreciate it.
Also, yes, I know that I can just catch the exception. My issue is, how do I handle the exception and still print the document?
EDIT:
It seems as if our program is having this issue in multiple places, not just with this particular set of code. It makes me think that the issue might not be in this code specifically. I'm working on it now and I kind of have to get it fixed, so when I figure out the solution, I'll be sure to post back. Until then, I'm always open to suggestions.
Not sure, but could it be because the code is deleting the default device context associated with the default printer? Try removing the dc.DeleteDC() line to give you:
if (dlg.DoModal() == IDOK)
{
dc.Detach();
dc.Attach(dlg.GetPrinterDC());
}
else
return;