From e910c7d9bf8e981bb1b7458c4793c5aedee98850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 8 Jan 2022 22:38:55 +0100 Subject: configure: Allow passing extra Objective C compiler flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can pass C/CPP/LD flags via CFLAGS/CXXFLAGS/LDFLAGS environment variables, or via configure --extra-cflags / --extra-cxxflags / --extra-ldflags options. Provide similar behavior for Objective C: use existing flags from $OBJCFLAGS, or passed via --extra-objcflags. Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Signed-off-by: Philippe Mathieu-Daudé --- configure | 8 ++++++++ meson.build | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/configure b/configure index 8860003..2bf40d9 100755 --- a/configure +++ b/configure @@ -285,6 +285,7 @@ done EXTRA_CFLAGS="" EXTRA_CXXFLAGS="" +EXTRA_OBJCFLAGS="" EXTRA_LDFLAGS="" xen_ctrl_version="$default_feature" @@ -366,9 +367,12 @@ for opt do --extra-cflags=*) EXTRA_CFLAGS="$EXTRA_CFLAGS $optarg" EXTRA_CXXFLAGS="$EXTRA_CXXFLAGS $optarg" + EXTRA_OBJCFLAGS="$EXTRA_OBJCFLAGS $optarg" ;; --extra-cxxflags=*) EXTRA_CXXFLAGS="$EXTRA_CXXFLAGS $optarg" ;; + --extra-objcflags=*) EXTRA_OBJCFLAGS="$EXTRA_OBJCFLAGS $optarg" + ;; --extra-ldflags=*) EXTRA_LDFLAGS="$EXTRA_LDFLAGS $optarg" ;; --enable-debug-info) debug_info="yes" @@ -748,6 +752,8 @@ for opt do ;; --extra-cxxflags=*) ;; + --extra-objcflags=*) + ;; --extra-ldflags=*) ;; --enable-debug-info) @@ -1172,6 +1178,7 @@ Advanced options (experts only): --objcc=OBJCC use Objective-C compiler OBJCC [$objcc] --extra-cflags=CFLAGS append extra C compiler flags CFLAGS --extra-cxxflags=CXXFLAGS append extra C++ compiler flags CXXFLAGS + --extra-objcflags=OBJCFLAGS append extra Objective C compiler flags OBJCFLAGS --extra-ldflags=LDFLAGS append extra linker flags LDFLAGS --cross-cc-ARCH=CC use compiler when building ARCH guest test cases --cross-cc-cflags-ARCH= use compiler flags when building ARCH guest tests @@ -3137,6 +3144,7 @@ if test "$skip_meson" = no; then echo "[built-in options]" >> $cross echo "c_args = [$(meson_quote $CFLAGS $EXTRA_CFLAGS)]" >> $cross echo "cpp_args = [$(meson_quote $CXXFLAGS $EXTRA_CXXFLAGS)]" >> $cross + test -n "$objcc" && echo "objc_args = [$(meson_quote $OBJCFLAGS $EXTRA_OBJCFLAGS)]" >> $cross echo "c_link_args = [$(meson_quote $CFLAGS $LDFLAGS $EXTRA_CFLAGS $EXTRA_LDFLAGS)]" >> $cross echo "cpp_link_args = [$(meson_quote $CXXFLAGS $LDFLAGS $EXTRA_CXXFLAGS $EXTRA_LDFLAGS)]" >> $cross echo "[binaries]" >> $cross diff --git a/meson.build b/meson.build index 2d66014..351f9f4 100644 --- a/meson.build +++ b/meson.build @@ -3467,6 +3467,11 @@ if link_language == 'cpp' + ['-O' + get_option('optimization')] + (get_option('debug') ? ['-g'] : []))} endif +if targetos == 'darwin' + summary_info += {'OBJCFLAGS': ' '.join(get_option('objc_args') + + ['-O' + get_option('optimization')] + + (get_option('debug') ? ['-g'] : []))} +endif link_args = get_option(link_language + '_link_args') if link_args.length() > 0 summary_info += {'LDFLAGS': ' '.join(link_args)} -- cgit v1.1 From 004900acbcde82d52d80404dea7f43ce5f8b78fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 10 Feb 2022 15:14:39 +0100 Subject: tests/fp/berkeley-testfloat-3: Ignore ignored #pragma directives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we already use -Wno-unknown-pragmas, we can also use -Wno-ignored-pragmas. This silences hundred of warnings using clang 13 on macOS Monterey: [409/771] Compiling C object tests/fp/libtestfloat.a.p/berkeley-testfloat-3_source_test_az_f128_rx.c.o ../tests/fp/berkeley-testfloat-3/source/test_az_f128_rx.c:49:14: warning: '#pragma FENV_ACCESS' is not supported on this target - ignored [-Wignored-pragmas] #pragma STDC FENV_ACCESS ON ^ 1 warning generated. Having: $ cc -v Apple clang version 13.0.0 (clang-1300.0.29.30) Reported-by: Roman Bolshakov Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Acked-by: Alex Bennée Signed-off-by: Philippe Mathieu-Daudé --- tests/fp/meson.build | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/fp/meson.build b/tests/fp/meson.build index 59776a0..8bd0979 100644 --- a/tests/fp/meson.build +++ b/tests/fp/meson.build @@ -37,6 +37,11 @@ tfcflags = [ '-Wno-error', ] +if cc.get_id() == 'clang' + # Clang does not support '#pragma STDC FENV_ACCESS' + tfcflags += [ '-Wno-ignored-pragmas' ] +endif + tfgencases = [ tfdir / 'genCases_ui32.c', tfdir / 'genCases_ui64.c', -- cgit v1.1 From 704afe34d8c4dbee3e88cfaa25c8561364169695 Mon Sep 17 00:00:00 2001 From: Cameron Esfahani Date: Thu, 28 Oct 2021 18:33:15 -0700 Subject: hvf: Use standard CR0 and CR4 register definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to have our own definitions of these registers. Signed-off-by: Cameron Esfahani Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé --- target/i386/hvf/vmx.h | 17 +++++++++-------- target/i386/hvf/x86.c | 6 +++--- target/i386/hvf/x86.h | 34 ---------------------------------- target/i386/hvf/x86_mmu.c | 2 +- target/i386/hvf/x86_task.c | 3 ++- 5 files changed, 15 insertions(+), 47 deletions(-) diff --git a/target/i386/hvf/vmx.h b/target/i386/hvf/vmx.h index 6df8711..29b7dee 100644 --- a/target/i386/hvf/vmx.h +++ b/target/i386/hvf/vmx.h @@ -124,10 +124,11 @@ static inline void macvm_set_cr0(hv_vcpuid_t vcpu, uint64_t cr0) uint64_t efer = rvmcs(vcpu, VMCS_GUEST_IA32_EFER); uint64_t old_cr0 = rvmcs(vcpu, VMCS_GUEST_CR0); uint64_t changed_cr0 = old_cr0 ^ cr0; - uint64_t mask = CR0_PG | CR0_CD | CR0_NW | CR0_NE | CR0_ET; + uint64_t mask = CR0_PG_MASK | CR0_CD_MASK | CR0_NW_MASK | + CR0_NE_MASK | CR0_ET_MASK; uint64_t entry_ctls; - if ((cr0 & CR0_PG) && (rvmcs(vcpu, VMCS_GUEST_CR4) & CR4_PAE) && + if ((cr0 & CR0_PG_MASK) && (rvmcs(vcpu, VMCS_GUEST_CR4) & CR4_PAE_MASK) && !(efer & MSR_EFER_LME)) { address_space_read(&address_space_memory, rvmcs(vcpu, VMCS_GUEST_CR3) & ~0x1f, @@ -142,8 +143,8 @@ static inline void macvm_set_cr0(hv_vcpuid_t vcpu, uint64_t cr0) wvmcs(vcpu, VMCS_CR0_SHADOW, cr0); if (efer & MSR_EFER_LME) { - if (changed_cr0 & CR0_PG) { - if (cr0 & CR0_PG) { + if (changed_cr0 & CR0_PG_MASK) { + if (cr0 & CR0_PG_MASK) { enter_long_mode(vcpu, cr0, efer); } else { exit_long_mode(vcpu, cr0, efer); @@ -155,8 +156,8 @@ static inline void macvm_set_cr0(hv_vcpuid_t vcpu, uint64_t cr0) } /* Filter new CR0 after we are finished examining it above. */ - cr0 = (cr0 & ~(mask & ~CR0_PG)); - wvmcs(vcpu, VMCS_GUEST_CR0, cr0 | CR0_NE | CR0_ET); + cr0 = (cr0 & ~(mask & ~CR0_PG_MASK)); + wvmcs(vcpu, VMCS_GUEST_CR0, cr0 | CR0_NE_MASK | CR0_ET_MASK); hv_vcpu_invalidate_tlb(vcpu); hv_vcpu_flush(vcpu); @@ -164,11 +165,11 @@ static inline void macvm_set_cr0(hv_vcpuid_t vcpu, uint64_t cr0) static inline void macvm_set_cr4(hv_vcpuid_t vcpu, uint64_t cr4) { - uint64_t guest_cr4 = cr4 | CR4_VMXE; + uint64_t guest_cr4 = cr4 | CR4_VMXE_MASK; wvmcs(vcpu, VMCS_GUEST_CR4, guest_cr4); wvmcs(vcpu, VMCS_CR4_SHADOW, cr4); - wvmcs(vcpu, VMCS_CR4_MASK, CR4_VMXE); + wvmcs(vcpu, VMCS_CR4_MASK, CR4_VMXE_MASK); hv_vcpu_invalidate_tlb(vcpu); hv_vcpu_flush(vcpu); diff --git a/target/i386/hvf/x86.c b/target/i386/hvf/x86.c index 2898bb7..91a3fe0 100644 --- a/target/i386/hvf/x86.c +++ b/target/i386/hvf/x86.c @@ -119,7 +119,7 @@ bool x86_read_call_gate(struct CPUState *cpu, struct x86_call_gate *idt_desc, bool x86_is_protected(struct CPUState *cpu) { uint64_t cr0 = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR0); - return cr0 & CR0_PE; + return cr0 & CR0_PE_MASK; } bool x86_is_real(struct CPUState *cpu) @@ -150,13 +150,13 @@ bool x86_is_long64_mode(struct CPUState *cpu) bool x86_is_paging_mode(struct CPUState *cpu) { uint64_t cr0 = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR0); - return cr0 & CR0_PG; + return cr0 & CR0_PG_MASK; } bool x86_is_pae_enabled(struct CPUState *cpu) { uint64_t cr4 = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR4); - return cr4 & CR4_PAE; + return cr4 & CR4_PAE_MASK; } target_ulong linear_addr(struct CPUState *cpu, target_ulong addr, X86Seg seg) diff --git a/target/i386/hvf/x86.h b/target/i386/hvf/x86.h index 782664c..947b98d 100644 --- a/target/i386/hvf/x86.h +++ b/target/i386/hvf/x86.h @@ -42,40 +42,6 @@ typedef struct x86_register { }; } __attribute__ ((__packed__)) x86_register; -typedef enum x86_reg_cr0 { - CR0_PE = (1L << 0), - CR0_MP = (1L << 1), - CR0_EM = (1L << 2), - CR0_TS = (1L << 3), - CR0_ET = (1L << 4), - CR0_NE = (1L << 5), - CR0_WP = (1L << 16), - CR0_AM = (1L << 18), - CR0_NW = (1L << 29), - CR0_CD = (1L << 30), - CR0_PG = (1L << 31), -} x86_reg_cr0; - -typedef enum x86_reg_cr4 { - CR4_VME = (1L << 0), - CR4_PVI = (1L << 1), - CR4_TSD = (1L << 2), - CR4_DE = (1L << 3), - CR4_PSE = (1L << 4), - CR4_PAE = (1L << 5), - CR4_MSE = (1L << 6), - CR4_PGE = (1L << 7), - CR4_PCE = (1L << 8), - CR4_OSFXSR = (1L << 9), - CR4_OSXMMEXCPT = (1L << 10), - CR4_VMXE = (1L << 13), - CR4_SMXE = (1L << 14), - CR4_FSGSBASE = (1L << 16), - CR4_PCIDE = (1L << 17), - CR4_OSXSAVE = (1L << 18), - CR4_SMEP = (1L << 20), -} x86_reg_cr4; - /* 16 bit Task State Segment */ typedef struct x86_tss_segment16 { uint16_t link; diff --git a/target/i386/hvf/x86_mmu.c b/target/i386/hvf/x86_mmu.c index e9ed0f5..df0b91c 100644 --- a/target/i386/hvf/x86_mmu.c +++ b/target/i386/hvf/x86_mmu.c @@ -129,7 +129,7 @@ static bool test_pt_entry(struct CPUState *cpu, struct gpt_translation *pt, uint32_t cr0 = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR0); /* check protection */ - if (cr0 & CR0_WP) { + if (cr0 & CR0_WP_MASK) { if (pt->write_access && !pte_write_access(pte)) { return false; } diff --git a/target/i386/hvf/x86_task.c b/target/i386/hvf/x86_task.c index 4221561..e130159 100644 --- a/target/i386/hvf/x86_task.c +++ b/target/i386/hvf/x86_task.c @@ -174,7 +174,8 @@ void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int rea //ret = task_switch_16(cpu, tss_sel, old_tss_sel, old_tss_base, &next_tss_desc); VM_PANIC("task_switch_16"); - macvm_set_cr0(cpu->hvf->fd, rvmcs(cpu->hvf->fd, VMCS_GUEST_CR0) | CR0_TS); + macvm_set_cr0(cpu->hvf->fd, rvmcs(cpu->hvf->fd, VMCS_GUEST_CR0) | + CR0_TS_MASK); x86_segment_descriptor_to_vmx(cpu, tss_sel, &next_tss_desc, &vmx_seg); vmx_write_segment_descriptor(cpu, &vmx_seg, R_TR); -- cgit v1.1 From 40eab4d95939c47f5bf6e664868c172090856244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 8 Jan 2022 16:58:10 +0100 Subject: hvf: Make hvf_get_segments() / hvf_put_segments() local MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both hvf_get_segments/hvf_put_segments() functions are only used within x86hvf.c: do not declare them as public API. Reviewed-by: Roman Bolshakov Tested-by: Roman Bolshakov Signed-off-by: Philippe Mathieu-Daudé --- target/i386/hvf/x86hvf.c | 4 ++-- target/i386/hvf/x86hvf.h | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/target/i386/hvf/x86hvf.c b/target/i386/hvf/x86hvf.c index 05ec1bd..907f09f 100644 --- a/target/i386/hvf/x86hvf.c +++ b/target/i386/hvf/x86hvf.c @@ -83,7 +83,7 @@ void hvf_put_xsave(CPUState *cpu_state) } } -void hvf_put_segments(CPUState *cpu_state) +static void hvf_put_segments(CPUState *cpu_state) { CPUX86State *env = &X86_CPU(cpu_state)->env; struct vmx_segment seg; @@ -166,7 +166,7 @@ void hvf_get_xsave(CPUState *cpu_state) x86_cpu_xrstor_all_areas(X86_CPU(cpu_state), xsave, xsave_len); } -void hvf_get_segments(CPUState *cpu_state) +static void hvf_get_segments(CPUState *cpu_state) { CPUX86State *env = &X86_CPU(cpu_state)->env; diff --git a/target/i386/hvf/x86hvf.h b/target/i386/hvf/x86hvf.h index 99ed8d6..db6003d 100644 --- a/target/i386/hvf/x86hvf.h +++ b/target/i386/hvf/x86hvf.h @@ -26,11 +26,9 @@ void hvf_set_segment(struct CPUState *cpu, struct vmx_segment *vmx_seg, SegmentCache *qseg, bool is_tr); void hvf_get_segment(SegmentCache *qseg, struct vmx_segment *vmx_seg); void hvf_put_xsave(CPUState *cpu_state); -void hvf_put_segments(CPUState *cpu_state); void hvf_put_msrs(CPUState *cpu_state); void hvf_get_xsave(CPUState *cpu_state); void hvf_get_msrs(CPUState *cpu_state); void vmx_clear_int_window_exiting(CPUState *cpu); -void hvf_get_segments(CPUState *cpu_state); void vmx_update_tpr(CPUState *cpu); #endif -- cgit v1.1 From 2e84d8521f596689d96e07ac377d2af1479774ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 8 Jan 2022 16:59:49 +0100 Subject: hvf: Remove deprecated hv_vcpu_flush() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building on macOS 11 [*], we get: In file included from ../target/i386/hvf/hvf.c:59: ../target/i386/hvf/vmx.h:174:5: error: 'hv_vcpu_flush' is deprecated: first deprecated in macOS 11.0 - This API has no effect and always returns HV_UNSUPPORTED [-Werror,-Wdeprecated-declarations] hv_vcpu_flush(vcpu); ^ /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Hypervisor.framework/Headers/hv.h:364:20: note: 'hv_vcpu_flush' has been explicitly marked deprecated here extern hv_return_t hv_vcpu_flush(hv_vcpuid_t vcpu) ^ Since this call "has no effect", simply remove it ¯\_(ツ)_/¯ Not very useful deprecation doc: https://developer.apple.com/documentation/hypervisor/1441386-hv_vcpu_flush [*] Also 10.15 (Catalina): https://lore.kernel.org/qemu-devel/Yd3DmSqZ1SiJwd7P@roolebo.dev/ Reviewed-by: Roman Bolshakov Tested-by: Roman Bolshakov Signed-off-by: Philippe Mathieu-Daudé --- target/i386/hvf/vmx.h | 2 -- target/i386/hvf/x86_task.c | 1 - target/i386/hvf/x86hvf.c | 2 -- 3 files changed, 5 deletions(-) diff --git a/target/i386/hvf/vmx.h b/target/i386/hvf/vmx.h index 29b7dee..573ddc3 100644 --- a/target/i386/hvf/vmx.h +++ b/target/i386/hvf/vmx.h @@ -160,7 +160,6 @@ static inline void macvm_set_cr0(hv_vcpuid_t vcpu, uint64_t cr0) wvmcs(vcpu, VMCS_GUEST_CR0, cr0 | CR0_NE_MASK | CR0_ET_MASK); hv_vcpu_invalidate_tlb(vcpu); - hv_vcpu_flush(vcpu); } static inline void macvm_set_cr4(hv_vcpuid_t vcpu, uint64_t cr4) @@ -172,7 +171,6 @@ static inline void macvm_set_cr4(hv_vcpuid_t vcpu, uint64_t cr4) wvmcs(vcpu, VMCS_CR4_MASK, CR4_VMXE_MASK); hv_vcpu_invalidate_tlb(vcpu); - hv_vcpu_flush(vcpu); } static inline void macvm_set_rip(CPUState *cpu, uint64_t rip) diff --git a/target/i386/hvf/x86_task.c b/target/i386/hvf/x86_task.c index e130159..d24daf6 100644 --- a/target/i386/hvf/x86_task.c +++ b/target/i386/hvf/x86_task.c @@ -182,5 +182,4 @@ void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int rea store_regs(cpu); hv_vcpu_invalidate_tlb(cpu->hvf->fd); - hv_vcpu_flush(cpu->hvf->fd); } diff --git a/target/i386/hvf/x86hvf.c b/target/i386/hvf/x86hvf.c index 907f09f..bec9fc5 100644 --- a/target/i386/hvf/x86hvf.c +++ b/target/i386/hvf/x86hvf.c @@ -125,8 +125,6 @@ static void hvf_put_segments(CPUState *cpu_state) hvf_set_segment(cpu_state, &seg, &env->ldt, false); vmx_write_segment_descriptor(cpu_state, &seg, R_LDTR); - - hv_vcpu_flush(cpu_state->hvf->fd); } void hvf_put_msrs(CPUState *cpu_state) -- cgit v1.1 From aa44d3f6b8d2f08f09034f287efec8890633cc38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 6 Jan 2022 00:46:01 +0100 Subject: block/file-posix: Remove a deprecation warning on macOS 12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building on macOS 12 we get: block/file-posix.c:3335:18: warning: 'IOMasterPort' is deprecated: first deprecated in macOS 12.0 [-Wdeprecated-declarations] kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort ); ^~~~~~~~~~~~ IOMainPort Replace by IOMainPort, redefining it to IOMasterPort if not available. Suggested-by: Akihiko Odaki Reviewed-by: Christian Schoenebeck Reviewed by: Cameron Esfahani Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Signed-off-by: Philippe Mathieu-Daudé --- block/file-posix.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index c000a61..39a3d6d 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -3320,17 +3320,23 @@ BlockDriver bdrv_file = { #if defined(__APPLE__) && defined(__MACH__) static kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize, int flags); + +#if !defined(MAC_OS_VERSION_12_0) \ + || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0) +#define IOMainPort IOMasterPort +#endif + static char *FindEjectableOpticalMedia(io_iterator_t *mediaIterator) { kern_return_t kernResult = KERN_FAILURE; - mach_port_t masterPort; + mach_port_t mainPort; CFMutableDictionaryRef classesToMatch; const char *matching_array[] = {kIODVDMediaClass, kIOCDMediaClass}; char *mediaType = NULL; - kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort ); + kernResult = IOMainPort(MACH_PORT_NULL, &mainPort); if ( KERN_SUCCESS != kernResult ) { - printf( "IOMasterPort returned %d\n", kernResult ); + printf("IOMainPort returned %d\n", kernResult); } int index; @@ -3343,7 +3349,7 @@ static char *FindEjectableOpticalMedia(io_iterator_t *mediaIterator) } CFDictionarySetValue(classesToMatch, CFSTR(kIOMediaEjectableKey), kCFBooleanTrue); - kernResult = IOServiceGetMatchingServices(masterPort, classesToMatch, + kernResult = IOServiceGetMatchingServices(mainPort, classesToMatch, mediaIterator); if (kernResult != KERN_SUCCESS) { error_report("Note: IOServiceGetMatchingServices returned %d", -- cgit v1.1 From 9f56bd6dab1a73362aefb7fe7bb8e364410d9d1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 9 Jan 2022 14:30:03 +0100 Subject: audio/coreaudio: Remove a deprecation warning on macOS 12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building on macOS 12 we get: audio/coreaudio.c:50:5: error: 'kAudioObjectPropertyElementMaster' is deprecated: first deprecated in macOS 12.0 [-Werror,-Wdeprecated-declarations] kAudioObjectPropertyElementMaster ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ kAudioObjectPropertyElementMain /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/CoreAudio.framework/Headers/AudioHardwareBase.h:208:5: note: 'kAudioObjectPropertyElementMaster' has been explicitly marked deprecated here kAudioObjectPropertyElementMaster API_DEPRECATED_WITH_REPLACEMENT("kAudioObjectPropertyElementMain", macos(10.0, 12.0), ios(2.0, 15.0), watchos(1.0, 8.0), tvos(9.0, 15.0)) = kAudioObjectPropertyElementMain ^ Replace by kAudioObjectPropertyElementMain, redefining it to kAudioObjectPropertyElementMaster if not available. Suggested-by: Akihiko Odaki Suggested-by: Christian Schoenebeck Suggested-by: Roman Bolshakov Reviewed-by: Christian Schoenebeck Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Signed-off-by: Philippe Mathieu-Daudé --- audio/coreaudio.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/audio/coreaudio.c b/audio/coreaudio.c index 0f19d0c..23d7593 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.c @@ -44,10 +44,15 @@ typedef struct coreaudioVoiceOut { bool enabled; } coreaudioVoiceOut; +#if !defined(MAC_OS_VERSION_12_0) \ + || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0) +#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster +#endif + static const AudioObjectPropertyAddress voice_addr = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster + kAudioObjectPropertyElementMain }; static OSStatus coreaudio_get_voice(AudioDeviceID *id) @@ -69,7 +74,7 @@ static OSStatus coreaudio_get_framesizerange(AudioDeviceID id, AudioObjectPropertyAddress addr = { kAudioDevicePropertyBufferFrameSizeRange, kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster + kAudioObjectPropertyElementMain }; return AudioObjectGetPropertyData(id, @@ -86,7 +91,7 @@ static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize) AudioObjectPropertyAddress addr = { kAudioDevicePropertyBufferFrameSize, kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster + kAudioObjectPropertyElementMain }; return AudioObjectGetPropertyData(id, @@ -103,7 +108,7 @@ static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize) AudioObjectPropertyAddress addr = { kAudioDevicePropertyBufferFrameSize, kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster + kAudioObjectPropertyElementMain }; return AudioObjectSetPropertyData(id, @@ -121,7 +126,7 @@ static OSStatus coreaudio_set_streamformat(AudioDeviceID id, AudioObjectPropertyAddress addr = { kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster + kAudioObjectPropertyElementMain }; return AudioObjectSetPropertyData(id, @@ -138,7 +143,7 @@ static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) AudioObjectPropertyAddress addr = { kAudioDevicePropertyDeviceIsRunning, kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster + kAudioObjectPropertyElementMain }; return AudioObjectGetPropertyData(id, -- cgit v1.1 From c9c847481e70f2f5065605b6d21f7f8d72e54607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 3 Feb 2022 16:25:09 +0100 Subject: audio/dbus: Fix building with modules on macOS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When configuring QEMU with --enable-modules we get on macOS: --- stderr --- Dependency ui-dbus cannot be satisfied ui-dbus depends on pixman and opengl, so add these dependencies to audio-dbus. Fixes: 739362d420 ("audio: add "dbus" audio backend") Reviewed-by: Li Zhang Signed-off-by: Philippe Mathieu-Daudé --- audio/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/meson.build b/audio/meson.build index 0ac3791..d9b2955 100644 --- a/audio/meson.build +++ b/audio/meson.build @@ -28,7 +28,7 @@ endforeach if dbus_display module_ss = ss.source_set() - module_ss.add(when: gio, if_true: files('dbusaudio.c')) + module_ss.add(when: [gio, pixman, opengl, 'CONFIG_GIO'], if_true: files('dbusaudio.c')) audio_modules += {'dbus': module_ss} endif -- cgit v1.1 From 8e30d39bade3010387177ca23dbc2244352ed4a3 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 6 Mar 2022 15:32:02 +0900 Subject: audio: Log context for audio bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without this change audio_bug aborts when the bug condition is met, which discards following useful logs. Call abort after such logs. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20220306063202.27331-1-akihiko.odaki@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- audio/audio.c | 25 ++++++++++++------------- audio/audio_template.h | 27 ++++++++++++--------------- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/audio/audio.c b/audio/audio.c index a88572e..6bc313d 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -117,7 +117,6 @@ int audio_bug (const char *funcname, int cond) AUD_log (NULL, "I am sorry\n"); } AUD_log (NULL, "Context:\n"); - abort(); } return cond; @@ -138,7 +137,7 @@ static inline int audio_bits_to_index (int bits) default: audio_bug ("bits_to_index", 1); AUD_log (NULL, "invalid bits %d\n", bits); - return 0; + abort(); } } @@ -156,7 +155,7 @@ void *audio_calloc (const char *funcname, int nmemb, size_t size) AUD_log (NULL, "%s passed invalid arguments to audio_calloc\n", funcname); AUD_log (NULL, "nmemb=%d size=%zu (len=%zu)\n", nmemb, size, len); - return NULL; + abort(); } return g_malloc0 (len); @@ -543,7 +542,7 @@ static size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw) size_t live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw); if (audio_bug(__func__, live > hw->conv_buf->size)) { dolog("live=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size); - return 0; + abort(); } return live; } @@ -581,7 +580,7 @@ static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size) } if (audio_bug(__func__, live > hw->conv_buf->size)) { dolog("live_in=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size); - return 0; + abort(); } rpos = audio_ring_posb(hw->conv_buf->pos, live, hw->conv_buf->size); @@ -656,7 +655,7 @@ static size_t audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live) if (audio_bug(__func__, live > hw->mix_buf->size)) { dolog("live=%zu hw->mix_buf->size=%zu\n", live, hw->mix_buf->size); - return 0; + abort(); } return live; } @@ -706,7 +705,7 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size) live = sw->total_hw_samples_mixed; if (audio_bug(__func__, live > hwsamples)) { dolog("live=%zu hw->mix_buf->size=%zu\n", live, hwsamples); - return 0; + abort(); } if (live == hwsamples) { @@ -998,7 +997,7 @@ static size_t audio_get_avail (SWVoiceIn *sw) if (audio_bug(__func__, live > sw->hw->conv_buf->size)) { dolog("live=%zu sw->hw->conv_buf->size=%zu\n", live, sw->hw->conv_buf->size); - return 0; + abort(); } ldebug ( @@ -1028,7 +1027,7 @@ static size_t audio_get_free(SWVoiceOut *sw) if (audio_bug(__func__, live > sw->hw->mix_buf->size)) { dolog("live=%zu sw->hw->mix_buf->size=%zu\n", live, sw->hw->mix_buf->size); - return 0; + abort(); } dead = sw->hw->mix_buf->size - live; @@ -1170,7 +1169,7 @@ static void audio_run_out (AudioState *s) if (audio_bug(__func__, live > hw->mix_buf->size)) { dolog("live=%zu hw->mix_buf->size=%zu\n", live, hw->mix_buf->size); - continue; + abort(); } if (hw->pending_disable && !nb_live) { @@ -1203,7 +1202,7 @@ static void audio_run_out (AudioState *s) if (audio_bug(__func__, hw->mix_buf->pos >= hw->mix_buf->size)) { dolog("hw->mix_buf->pos=%zu hw->mix_buf->size=%zu played=%zu\n", hw->mix_buf->pos, hw->mix_buf->size, played); - hw->mix_buf->pos = 0; + abort(); } #ifdef DEBUG_OUT @@ -1223,7 +1222,7 @@ static void audio_run_out (AudioState *s) if (audio_bug(__func__, played > sw->total_hw_samples_mixed)) { dolog("played=%zu sw->total_hw_samples_mixed=%zu\n", played, sw->total_hw_samples_mixed); - played = sw->total_hw_samples_mixed; + abort(); } sw->total_hw_samples_mixed -= played; @@ -1346,7 +1345,7 @@ static void audio_run_capture (AudioState *s) if (audio_bug(__func__, captured > sw->total_hw_samples_mixed)) { dolog("captured=%zu sw->total_hw_samples_mixed=%zu\n", captured, sw->total_hw_samples_mixed); - captured = sw->total_hw_samples_mixed; + abort(); } sw->total_hw_samples_mixed -= captured; diff --git a/audio/audio_template.h b/audio/audio_template.h index d2d3486..7192b19 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -59,12 +59,13 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s, if (audio_bug(__func__, !voice_size && max_voices)) { dolog ("drv=`%s' voice_size=0 max_voices=%d\n", drv->name, max_voices); - glue (s->nb_hw_voices_, TYPE) = 0; + abort(); } if (audio_bug(__func__, voice_size && !max_voices)) { dolog ("drv=`%s' voice_size=%d max_voices=0\n", drv->name, voice_size); + abort(); } } @@ -81,6 +82,7 @@ static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw) size_t samples = hw->samples; if (audio_bug(__func__, samples == 0)) { dolog("Attempted to allocate empty buffer\n"); + abort(); } HWBUF = g_malloc0(sizeof(STSampleBuffer) + sizeof(st_sample) * samples); @@ -252,12 +254,12 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, if (audio_bug(__func__, !drv)) { dolog ("No host audio driver\n"); - return NULL; + abort(); } if (audio_bug(__func__, !drv->pcm_ops)) { dolog ("Host audio driver without pcm_ops\n"); - return NULL; + abort(); } hw = audio_calloc(__func__, 1, glue(drv->voice_size_, TYPE)); @@ -275,12 +277,13 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, QLIST_INIT (&hw->cap_head); #endif if (glue (hw->pcm_ops->init_, TYPE) (hw, as, s->drv_opaque)) { - goto err0; + g_free(hw); + return NULL; } if (audio_bug(__func__, hw->samples <= 0)) { dolog("hw->samples=%zd\n", hw->samples); - goto err1; + abort(); } if (hw->info.is_float) { @@ -309,12 +312,6 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, audio_attach_capture (hw); #endif return hw; - - err1: - glue (hw->pcm_ops->fini_, TYPE) (hw); - err0: - g_free (hw); - return NULL; } AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev) @@ -435,7 +432,7 @@ void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw) if (sw) { if (audio_bug(__func__, !card)) { dolog ("card=%p\n", card); - return; + abort(); } glue (audio_close_, TYPE) (sw); @@ -457,7 +454,7 @@ SW *glue (AUD_open_, TYPE) ( if (audio_bug(__func__, !card || !name || !callback_fn || !as)) { dolog ("card=%p name=%p callback_fn=%p as=%p\n", card, name, callback_fn, as); - goto fail; + abort(); } s = card->state; @@ -468,12 +465,12 @@ SW *glue (AUD_open_, TYPE) ( if (audio_bug(__func__, audio_validate_settings(as))) { audio_print_settings (as); - goto fail; + abort(); } if (audio_bug(__func__, !s->drv)) { dolog ("Can not open `%s' (no host audio driver)\n", name); - goto fail; + abort(); } if (sw && audio_pcm_info_eq (&sw->info, as)) { -- cgit v1.1 From 44ccb2dbe9047586e3fb94bb2ece222e112e5eaf Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 6 Mar 2022 21:34:10 +0900 Subject: coreaudio: Always return 0 in handle_voice_change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit handle_voice_change() is a CoreAudio callback function as of CoreAudio type AudioObjectPropertyListenerProc, and for the latter MacOSX.sdk/System/ Library/Frameworks/CoreAudio.framework/Headers/AudioHardware.h says "The return value is currently unused and should always be 0.". Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20220306123410.61063-1-akihiko.odaki@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- audio/coreaudio.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/audio/coreaudio.c b/audio/coreaudio.c index 23d7593..3186b68 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.c @@ -545,7 +545,6 @@ static OSStatus handle_voice_change( const AudioObjectPropertyAddress *in_addresses, void *in_client_data) { - OSStatus status; coreaudioVoiceOut *core = in_client_data; qemu_mutex_lock_iothread(); @@ -554,13 +553,12 @@ static OSStatus handle_voice_change( fini_out_device(core); } - status = init_out_device(core); - if (!status) { + if (!init_out_device(core)) { update_device_playback_state(core); } qemu_mutex_unlock_iothread(); - return status; + return 0; } static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, -- cgit v1.1 From 8b46d7e2dc8ec4b3515e43bc28ee77d9afab8b23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 15 Feb 2022 11:05:44 +0100 Subject: audio: Rename coreaudio extension to use Objective-C compiler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The coreaudio library includes Objective-C declarations (using the caret '^' symbol to declare block references [*]). When building with a C compiler we get: [175/839] Compiling C object libcommon.fa.p/audio_coreaudio.c.o In file included from /Library/Developer/CommandLineTools/SDKs/MacOSX12.sdk/System/Library/Frameworks/CoreAudio.framework/Headers/CoreAudio.h:18, from ../../audio/coreaudio.c:26: /Library/Developer/CommandLineTools/SDKs/MacOSX12.sdk/System/Library/Frameworks/CoreAudio.framework/Headers/AudioHardware.h:162:2: error: expected identifier or '(' before '^' token 162 | (^AudioObjectPropertyListenerBlock)( UInt32 inNumberAddresses, | ^ FAILED: libcommon.fa.p/audio_coreaudio.c.o Rename the file to use the Objective-C default extension (.m) so meson calls the correct compiler. [*] https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html Reviewed-by: Christian Schoenebeck Signed-off-by: Philippe Mathieu-Daudé --- audio/coreaudio.c | 687 ------------------------------------------------------ audio/coreaudio.m | 687 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ audio/meson.build | 2 +- 3 files changed, 688 insertions(+), 688 deletions(-) delete mode 100644 audio/coreaudio.c create mode 100644 audio/coreaudio.m diff --git a/audio/coreaudio.c b/audio/coreaudio.c deleted file mode 100644 index 3186b68..0000000 --- a/audio/coreaudio.c +++ /dev/null @@ -1,687 +0,0 @@ -/* - * QEMU OS X CoreAudio audio driver - * - * Copyright (c) 2005 Mike Kronenberg - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include -#include /* pthread_X */ - -#include "qemu/main-loop.h" -#include "qemu/module.h" -#include "audio.h" - -#define AUDIO_CAP "coreaudio" -#include "audio_int.h" - -typedef struct coreaudioVoiceOut { - HWVoiceOut hw; - pthread_mutex_t buf_mutex; - AudioDeviceID outputDeviceID; - int frameSizeSetting; - uint32_t bufferCount; - UInt32 audioDevicePropertyBufferFrameSize; - AudioDeviceIOProcID ioprocid; - bool enabled; -} coreaudioVoiceOut; - -#if !defined(MAC_OS_VERSION_12_0) \ - || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0) -#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster -#endif - -static const AudioObjectPropertyAddress voice_addr = { - kAudioHardwarePropertyDefaultOutputDevice, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMain -}; - -static OSStatus coreaudio_get_voice(AudioDeviceID *id) -{ - UInt32 size = sizeof(*id); - - return AudioObjectGetPropertyData(kAudioObjectSystemObject, - &voice_addr, - 0, - NULL, - &size, - id); -} - -static OSStatus coreaudio_get_framesizerange(AudioDeviceID id, - AudioValueRange *framerange) -{ - UInt32 size = sizeof(*framerange); - AudioObjectPropertyAddress addr = { - kAudioDevicePropertyBufferFrameSizeRange, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMain - }; - - return AudioObjectGetPropertyData(id, - &addr, - 0, - NULL, - &size, - framerange); -} - -static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize) -{ - UInt32 size = sizeof(*framesize); - AudioObjectPropertyAddress addr = { - kAudioDevicePropertyBufferFrameSize, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMain - }; - - return AudioObjectGetPropertyData(id, - &addr, - 0, - NULL, - &size, - framesize); -} - -static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize) -{ - UInt32 size = sizeof(*framesize); - AudioObjectPropertyAddress addr = { - kAudioDevicePropertyBufferFrameSize, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMain - }; - - return AudioObjectSetPropertyData(id, - &addr, - 0, - NULL, - size, - framesize); -} - -static OSStatus coreaudio_set_streamformat(AudioDeviceID id, - AudioStreamBasicDescription *d) -{ - UInt32 size = sizeof(*d); - AudioObjectPropertyAddress addr = { - kAudioDevicePropertyStreamFormat, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMain - }; - - return AudioObjectSetPropertyData(id, - &addr, - 0, - NULL, - size, - d); -} - -static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) -{ - UInt32 size = sizeof(*result); - AudioObjectPropertyAddress addr = { - kAudioDevicePropertyDeviceIsRunning, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMain - }; - - return AudioObjectGetPropertyData(id, - &addr, - 0, - NULL, - &size, - result); -} - -static void coreaudio_logstatus (OSStatus status) -{ - const char *str = "BUG"; - - switch (status) { - case kAudioHardwareNoError: - str = "kAudioHardwareNoError"; - break; - - case kAudioHardwareNotRunningError: - str = "kAudioHardwareNotRunningError"; - break; - - case kAudioHardwareUnspecifiedError: - str = "kAudioHardwareUnspecifiedError"; - break; - - case kAudioHardwareUnknownPropertyError: - str = "kAudioHardwareUnknownPropertyError"; - break; - - case kAudioHardwareBadPropertySizeError: - str = "kAudioHardwareBadPropertySizeError"; - break; - - case kAudioHardwareIllegalOperationError: - str = "kAudioHardwareIllegalOperationError"; - break; - - case kAudioHardwareBadDeviceError: - str = "kAudioHardwareBadDeviceError"; - break; - - case kAudioHardwareBadStreamError: - str = "kAudioHardwareBadStreamError"; - break; - - case kAudioHardwareUnsupportedOperationError: - str = "kAudioHardwareUnsupportedOperationError"; - break; - - case kAudioDeviceUnsupportedFormatError: - str = "kAudioDeviceUnsupportedFormatError"; - break; - - case kAudioDevicePermissionsError: - str = "kAudioDevicePermissionsError"; - break; - - default: - AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status); - return; - } - - AUD_log (AUDIO_CAP, "Reason: %s\n", str); -} - -static void GCC_FMT_ATTR (2, 3) coreaudio_logerr ( - OSStatus status, - const char *fmt, - ... - ) -{ - va_list ap; - - va_start (ap, fmt); - AUD_log (AUDIO_CAP, fmt, ap); - va_end (ap); - - coreaudio_logstatus (status); -} - -static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 ( - OSStatus status, - const char *typ, - const char *fmt, - ... - ) -{ - va_list ap; - - AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); - - va_start (ap, fmt); - AUD_vlog (AUDIO_CAP, fmt, ap); - va_end (ap); - - coreaudio_logstatus (status); -} - -#define coreaudio_playback_logerr(status, ...) \ - coreaudio_logerr2(status, "playback", __VA_ARGS__) - -static int coreaudio_buf_lock (coreaudioVoiceOut *core, const char *fn_name) -{ - int err; - - err = pthread_mutex_lock (&core->buf_mutex); - if (err) { - dolog ("Could not lock voice for %s\nReason: %s\n", - fn_name, strerror (err)); - return -1; - } - return 0; -} - -static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name) -{ - int err; - - err = pthread_mutex_unlock (&core->buf_mutex); - if (err) { - dolog ("Could not unlock voice for %s\nReason: %s\n", - fn_name, strerror (err)); - return -1; - } - return 0; -} - -#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \ - static ret_type glue(coreaudio_, name)args_decl \ - { \ - coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \ - ret_type ret; \ - \ - if (coreaudio_buf_lock(core, "coreaudio_" #name)) { \ - return 0; \ - } \ - \ - ret = glue(audio_generic_, name)args; \ - \ - coreaudio_buf_unlock(core, "coreaudio_" #name); \ - return ret; \ - } -COREAUDIO_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw)) -COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size), - (hw, size)) -COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t, - (HWVoiceOut *hw, void *buf, size_t size), - (hw, buf, size)) -COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size), - (hw, buf, size)) -#undef COREAUDIO_WRAPPER_FUNC - -/* - * callback to feed audiooutput buffer. called without iothread lock. - * allowed to lock "buf_mutex", but disallowed to have any other locks. - */ -static OSStatus audioDeviceIOProc( - AudioDeviceID inDevice, - const AudioTimeStamp *inNow, - const AudioBufferList *inInputData, - const AudioTimeStamp *inInputTime, - AudioBufferList *outOutputData, - const AudioTimeStamp *inOutputTime, - void *hwptr) -{ - UInt32 frameCount, pending_frames; - void *out = outOutputData->mBuffers[0].mData; - HWVoiceOut *hw = hwptr; - coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr; - size_t len; - - if (coreaudio_buf_lock (core, "audioDeviceIOProc")) { - inInputTime = 0; - return 0; - } - - if (inDevice != core->outputDeviceID) { - coreaudio_buf_unlock (core, "audioDeviceIOProc(old device)"); - return 0; - } - - frameCount = core->audioDevicePropertyBufferFrameSize; - pending_frames = hw->pending_emul / hw->info.bytes_per_frame; - - /* if there are not enough samples, set signal and return */ - if (pending_frames < frameCount) { - inInputTime = 0; - coreaudio_buf_unlock (core, "audioDeviceIOProc(empty)"); - return 0; - } - - len = frameCount * hw->info.bytes_per_frame; - while (len) { - size_t write_len, start; - - start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul); - assert(start < hw->size_emul); - - write_len = MIN(MIN(hw->pending_emul, len), - hw->size_emul - start); - - memcpy(out, hw->buf_emul + start, write_len); - hw->pending_emul -= write_len; - len -= write_len; - out += write_len; - } - - coreaudio_buf_unlock (core, "audioDeviceIOProc"); - return 0; -} - -static OSStatus init_out_device(coreaudioVoiceOut *core) -{ - OSStatus status; - AudioValueRange frameRange; - - AudioStreamBasicDescription streamBasicDescription = { - .mBitsPerChannel = core->hw.info.bits, - .mBytesPerFrame = core->hw.info.bytes_per_frame, - .mBytesPerPacket = core->hw.info.bytes_per_frame, - .mChannelsPerFrame = core->hw.info.nchannels, - .mFormatFlags = kLinearPCMFormatFlagIsFloat, - .mFormatID = kAudioFormatLinearPCM, - .mFramesPerPacket = 1, - .mSampleRate = core->hw.info.freq - }; - - status = coreaudio_get_voice(&core->outputDeviceID); - if (status != kAudioHardwareNoError) { - coreaudio_playback_logerr (status, - "Could not get default output Device\n"); - return status; - } - if (core->outputDeviceID == kAudioDeviceUnknown) { - dolog ("Could not initialize playback - Unknown Audiodevice\n"); - return status; - } - - /* get minimum and maximum buffer frame sizes */ - status = coreaudio_get_framesizerange(core->outputDeviceID, - &frameRange); - if (status == kAudioHardwareBadObjectError) { - return 0; - } - if (status != kAudioHardwareNoError) { - coreaudio_playback_logerr (status, - "Could not get device buffer frame range\n"); - return status; - } - - if (frameRange.mMinimum > core->frameSizeSetting) { - core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; - dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); - } else if (frameRange.mMaximum < core->frameSizeSetting) { - core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; - dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); - } else { - core->audioDevicePropertyBufferFrameSize = core->frameSizeSetting; - } - - /* set Buffer Frame Size */ - status = coreaudio_set_framesize(core->outputDeviceID, - &core->audioDevicePropertyBufferFrameSize); - if (status == kAudioHardwareBadObjectError) { - return 0; - } - if (status != kAudioHardwareNoError) { - coreaudio_playback_logerr (status, - "Could not set device buffer frame size %" PRIu32 "\n", - (uint32_t)core->audioDevicePropertyBufferFrameSize); - return status; - } - - /* get Buffer Frame Size */ - status = coreaudio_get_framesize(core->outputDeviceID, - &core->audioDevicePropertyBufferFrameSize); - if (status == kAudioHardwareBadObjectError) { - return 0; - } - if (status != kAudioHardwareNoError) { - coreaudio_playback_logerr (status, - "Could not get device buffer frame size\n"); - return status; - } - core->hw.samples = core->bufferCount * core->audioDevicePropertyBufferFrameSize; - - /* set Samplerate */ - status = coreaudio_set_streamformat(core->outputDeviceID, - &streamBasicDescription); - if (status == kAudioHardwareBadObjectError) { - return 0; - } - if (status != kAudioHardwareNoError) { - coreaudio_playback_logerr (status, - "Could not set samplerate %lf\n", - streamBasicDescription.mSampleRate); - core->outputDeviceID = kAudioDeviceUnknown; - return status; - } - - /* - * set Callback. - * - * On macOS 11.3.1, Core Audio calls AudioDeviceIOProc after calling an - * internal function named HALB_Mutex::Lock(), which locks a mutex in - * HALB_IOThread::Entry(void*). HALB_Mutex::Lock() is also called in - * AudioObjectGetPropertyData, which is called by coreaudio driver. - * Therefore, the specified callback must be designed to avoid a deadlock - * with the callers of AudioObjectGetPropertyData. - */ - core->ioprocid = NULL; - status = AudioDeviceCreateIOProcID(core->outputDeviceID, - audioDeviceIOProc, - &core->hw, - &core->ioprocid); - if (status == kAudioHardwareBadDeviceError) { - return 0; - } - if (status != kAudioHardwareNoError || core->ioprocid == NULL) { - coreaudio_playback_logerr (status, "Could not set IOProc\n"); - core->outputDeviceID = kAudioDeviceUnknown; - return status; - } - - return 0; -} - -static void fini_out_device(coreaudioVoiceOut *core) -{ - OSStatus status; - UInt32 isrunning; - - /* stop playback */ - status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning); - if (status != kAudioHardwareBadObjectError) { - if (status != kAudioHardwareNoError) { - coreaudio_logerr(status, - "Could not determine whether Device is playing\n"); - } - - if (isrunning) { - status = AudioDeviceStop(core->outputDeviceID, core->ioprocid); - if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { - coreaudio_logerr(status, "Could not stop playback\n"); - } - } - } - - /* remove callback */ - status = AudioDeviceDestroyIOProcID(core->outputDeviceID, - core->ioprocid); - if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { - coreaudio_logerr(status, "Could not remove IOProc\n"); - } - core->outputDeviceID = kAudioDeviceUnknown; -} - -static void update_device_playback_state(coreaudioVoiceOut *core) -{ - OSStatus status; - UInt32 isrunning; - - status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning); - if (status != kAudioHardwareNoError) { - if (status != kAudioHardwareBadObjectError) { - coreaudio_logerr(status, - "Could not determine whether Device is playing\n"); - } - - return; - } - - if (core->enabled) { - /* start playback */ - if (!isrunning) { - status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); - if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { - coreaudio_logerr (status, "Could not resume playback\n"); - } - } - } else { - /* stop playback */ - if (isrunning) { - status = AudioDeviceStop(core->outputDeviceID, - core->ioprocid); - if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { - coreaudio_logerr(status, "Could not pause playback\n"); - } - } - } -} - -/* called without iothread lock. */ -static OSStatus handle_voice_change( - AudioObjectID in_object_id, - UInt32 in_number_addresses, - const AudioObjectPropertyAddress *in_addresses, - void *in_client_data) -{ - coreaudioVoiceOut *core = in_client_data; - - qemu_mutex_lock_iothread(); - - if (core->outputDeviceID) { - fini_out_device(core); - } - - if (!init_out_device(core)) { - update_device_playback_state(core); - } - - qemu_mutex_unlock_iothread(); - return 0; -} - -static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, - void *drv_opaque) -{ - OSStatus status; - coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; - int err; - Audiodev *dev = drv_opaque; - AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out; - struct audsettings obt_as; - - /* create mutex */ - err = pthread_mutex_init(&core->buf_mutex, NULL); - if (err) { - dolog("Could not create mutex\nReason: %s\n", strerror (err)); - return -1; - } - - obt_as = *as; - as = &obt_as; - as->fmt = AUDIO_FORMAT_F32; - audio_pcm_init_info (&hw->info, as); - - core->frameSizeSetting = audio_buffer_frames( - qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610); - - core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4; - - status = AudioObjectAddPropertyListener(kAudioObjectSystemObject, - &voice_addr, handle_voice_change, - core); - if (status != kAudioHardwareNoError) { - coreaudio_playback_logerr (status, - "Could not listen to voice property change\n"); - return -1; - } - - if (init_out_device(core)) { - status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, - &voice_addr, - handle_voice_change, - core); - if (status != kAudioHardwareNoError) { - coreaudio_playback_logerr(status, - "Could not remove voice property change listener\n"); - } - - return -1; - } - - return 0; -} - -static void coreaudio_fini_out (HWVoiceOut *hw) -{ - OSStatus status; - int err; - coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; - - status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, - &voice_addr, - handle_voice_change, - core); - if (status != kAudioHardwareNoError) { - coreaudio_logerr(status, "Could not remove voice property change listener\n"); - } - - fini_out_device(core); - - /* destroy mutex */ - err = pthread_mutex_destroy(&core->buf_mutex); - if (err) { - dolog("Could not destroy mutex\nReason: %s\n", strerror (err)); - } -} - -static void coreaudio_enable_out(HWVoiceOut *hw, bool enable) -{ - coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; - - core->enabled = enable; - update_device_playback_state(core); -} - -static void *coreaudio_audio_init(Audiodev *dev) -{ - return dev; -} - -static void coreaudio_audio_fini (void *opaque) -{ -} - -static struct audio_pcm_ops coreaudio_pcm_ops = { - .init_out = coreaudio_init_out, - .fini_out = coreaudio_fini_out, - /* wrapper for audio_generic_write */ - .write = coreaudio_write, - /* wrapper for audio_generic_buffer_get_free */ - .buffer_get_free = coreaudio_buffer_get_free, - /* wrapper for audio_generic_get_buffer_out */ - .get_buffer_out = coreaudio_get_buffer_out, - /* wrapper for audio_generic_put_buffer_out */ - .put_buffer_out = coreaudio_put_buffer_out, - .enable_out = coreaudio_enable_out -}; - -static struct audio_driver coreaudio_audio_driver = { - .name = "coreaudio", - .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html", - .init = coreaudio_audio_init, - .fini = coreaudio_audio_fini, - .pcm_ops = &coreaudio_pcm_ops, - .can_be_default = 1, - .max_voices_out = 1, - .max_voices_in = 0, - .voice_size_out = sizeof (coreaudioVoiceOut), - .voice_size_in = 0 -}; - -static void register_audio_coreaudio(void) -{ - audio_driver_register(&coreaudio_audio_driver); -} -type_init(register_audio_coreaudio); diff --git a/audio/coreaudio.m b/audio/coreaudio.m new file mode 100644 index 0000000..3186b68 --- /dev/null +++ b/audio/coreaudio.m @@ -0,0 +1,687 @@ +/* + * QEMU OS X CoreAudio audio driver + * + * Copyright (c) 2005 Mike Kronenberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include +#include /* pthread_X */ + +#include "qemu/main-loop.h" +#include "qemu/module.h" +#include "audio.h" + +#define AUDIO_CAP "coreaudio" +#include "audio_int.h" + +typedef struct coreaudioVoiceOut { + HWVoiceOut hw; + pthread_mutex_t buf_mutex; + AudioDeviceID outputDeviceID; + int frameSizeSetting; + uint32_t bufferCount; + UInt32 audioDevicePropertyBufferFrameSize; + AudioDeviceIOProcID ioprocid; + bool enabled; +} coreaudioVoiceOut; + +#if !defined(MAC_OS_VERSION_12_0) \ + || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0) +#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster +#endif + +static const AudioObjectPropertyAddress voice_addr = { + kAudioHardwarePropertyDefaultOutputDevice, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMain +}; + +static OSStatus coreaudio_get_voice(AudioDeviceID *id) +{ + UInt32 size = sizeof(*id); + + return AudioObjectGetPropertyData(kAudioObjectSystemObject, + &voice_addr, + 0, + NULL, + &size, + id); +} + +static OSStatus coreaudio_get_framesizerange(AudioDeviceID id, + AudioValueRange *framerange) +{ + UInt32 size = sizeof(*framerange); + AudioObjectPropertyAddress addr = { + kAudioDevicePropertyBufferFrameSizeRange, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMain + }; + + return AudioObjectGetPropertyData(id, + &addr, + 0, + NULL, + &size, + framerange); +} + +static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize) +{ + UInt32 size = sizeof(*framesize); + AudioObjectPropertyAddress addr = { + kAudioDevicePropertyBufferFrameSize, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMain + }; + + return AudioObjectGetPropertyData(id, + &addr, + 0, + NULL, + &size, + framesize); +} + +static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize) +{ + UInt32 size = sizeof(*framesize); + AudioObjectPropertyAddress addr = { + kAudioDevicePropertyBufferFrameSize, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMain + }; + + return AudioObjectSetPropertyData(id, + &addr, + 0, + NULL, + size, + framesize); +} + +static OSStatus coreaudio_set_streamformat(AudioDeviceID id, + AudioStreamBasicDescription *d) +{ + UInt32 size = sizeof(*d); + AudioObjectPropertyAddress addr = { + kAudioDevicePropertyStreamFormat, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMain + }; + + return AudioObjectSetPropertyData(id, + &addr, + 0, + NULL, + size, + d); +} + +static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) +{ + UInt32 size = sizeof(*result); + AudioObjectPropertyAddress addr = { + kAudioDevicePropertyDeviceIsRunning, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMain + }; + + return AudioObjectGetPropertyData(id, + &addr, + 0, + NULL, + &size, + result); +} + +static void coreaudio_logstatus (OSStatus status) +{ + const char *str = "BUG"; + + switch (status) { + case kAudioHardwareNoError: + str = "kAudioHardwareNoError"; + break; + + case kAudioHardwareNotRunningError: + str = "kAudioHardwareNotRunningError"; + break; + + case kAudioHardwareUnspecifiedError: + str = "kAudioHardwareUnspecifiedError"; + break; + + case kAudioHardwareUnknownPropertyError: + str = "kAudioHardwareUnknownPropertyError"; + break; + + case kAudioHardwareBadPropertySizeError: + str = "kAudioHardwareBadPropertySizeError"; + break; + + case kAudioHardwareIllegalOperationError: + str = "kAudioHardwareIllegalOperationError"; + break; + + case kAudioHardwareBadDeviceError: + str = "kAudioHardwareBadDeviceError"; + break; + + case kAudioHardwareBadStreamError: + str = "kAudioHardwareBadStreamError"; + break; + + case kAudioHardwareUnsupportedOperationError: + str = "kAudioHardwareUnsupportedOperationError"; + break; + + case kAudioDeviceUnsupportedFormatError: + str = "kAudioDeviceUnsupportedFormatError"; + break; + + case kAudioDevicePermissionsError: + str = "kAudioDevicePermissionsError"; + break; + + default: + AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status); + return; + } + + AUD_log (AUDIO_CAP, "Reason: %s\n", str); +} + +static void GCC_FMT_ATTR (2, 3) coreaudio_logerr ( + OSStatus status, + const char *fmt, + ... + ) +{ + va_list ap; + + va_start (ap, fmt); + AUD_log (AUDIO_CAP, fmt, ap); + va_end (ap); + + coreaudio_logstatus (status); +} + +static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 ( + OSStatus status, + const char *typ, + const char *fmt, + ... + ) +{ + va_list ap; + + AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + coreaudio_logstatus (status); +} + +#define coreaudio_playback_logerr(status, ...) \ + coreaudio_logerr2(status, "playback", __VA_ARGS__) + +static int coreaudio_buf_lock (coreaudioVoiceOut *core, const char *fn_name) +{ + int err; + + err = pthread_mutex_lock (&core->buf_mutex); + if (err) { + dolog ("Could not lock voice for %s\nReason: %s\n", + fn_name, strerror (err)); + return -1; + } + return 0; +} + +static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name) +{ + int err; + + err = pthread_mutex_unlock (&core->buf_mutex); + if (err) { + dolog ("Could not unlock voice for %s\nReason: %s\n", + fn_name, strerror (err)); + return -1; + } + return 0; +} + +#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \ + static ret_type glue(coreaudio_, name)args_decl \ + { \ + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \ + ret_type ret; \ + \ + if (coreaudio_buf_lock(core, "coreaudio_" #name)) { \ + return 0; \ + } \ + \ + ret = glue(audio_generic_, name)args; \ + \ + coreaudio_buf_unlock(core, "coreaudio_" #name); \ + return ret; \ + } +COREAUDIO_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw)) +COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size), + (hw, size)) +COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t, + (HWVoiceOut *hw, void *buf, size_t size), + (hw, buf, size)) +COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size), + (hw, buf, size)) +#undef COREAUDIO_WRAPPER_FUNC + +/* + * callback to feed audiooutput buffer. called without iothread lock. + * allowed to lock "buf_mutex", but disallowed to have any other locks. + */ +static OSStatus audioDeviceIOProc( + AudioDeviceID inDevice, + const AudioTimeStamp *inNow, + const AudioBufferList *inInputData, + const AudioTimeStamp *inInputTime, + AudioBufferList *outOutputData, + const AudioTimeStamp *inOutputTime, + void *hwptr) +{ + UInt32 frameCount, pending_frames; + void *out = outOutputData->mBuffers[0].mData; + HWVoiceOut *hw = hwptr; + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr; + size_t len; + + if (coreaudio_buf_lock (core, "audioDeviceIOProc")) { + inInputTime = 0; + return 0; + } + + if (inDevice != core->outputDeviceID) { + coreaudio_buf_unlock (core, "audioDeviceIOProc(old device)"); + return 0; + } + + frameCount = core->audioDevicePropertyBufferFrameSize; + pending_frames = hw->pending_emul / hw->info.bytes_per_frame; + + /* if there are not enough samples, set signal and return */ + if (pending_frames < frameCount) { + inInputTime = 0; + coreaudio_buf_unlock (core, "audioDeviceIOProc(empty)"); + return 0; + } + + len = frameCount * hw->info.bytes_per_frame; + while (len) { + size_t write_len, start; + + start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul); + assert(start < hw->size_emul); + + write_len = MIN(MIN(hw->pending_emul, len), + hw->size_emul - start); + + memcpy(out, hw->buf_emul + start, write_len); + hw->pending_emul -= write_len; + len -= write_len; + out += write_len; + } + + coreaudio_buf_unlock (core, "audioDeviceIOProc"); + return 0; +} + +static OSStatus init_out_device(coreaudioVoiceOut *core) +{ + OSStatus status; + AudioValueRange frameRange; + + AudioStreamBasicDescription streamBasicDescription = { + .mBitsPerChannel = core->hw.info.bits, + .mBytesPerFrame = core->hw.info.bytes_per_frame, + .mBytesPerPacket = core->hw.info.bytes_per_frame, + .mChannelsPerFrame = core->hw.info.nchannels, + .mFormatFlags = kLinearPCMFormatFlagIsFloat, + .mFormatID = kAudioFormatLinearPCM, + .mFramesPerPacket = 1, + .mSampleRate = core->hw.info.freq + }; + + status = coreaudio_get_voice(&core->outputDeviceID); + if (status != kAudioHardwareNoError) { + coreaudio_playback_logerr (status, + "Could not get default output Device\n"); + return status; + } + if (core->outputDeviceID == kAudioDeviceUnknown) { + dolog ("Could not initialize playback - Unknown Audiodevice\n"); + return status; + } + + /* get minimum and maximum buffer frame sizes */ + status = coreaudio_get_framesizerange(core->outputDeviceID, + &frameRange); + if (status == kAudioHardwareBadObjectError) { + return 0; + } + if (status != kAudioHardwareNoError) { + coreaudio_playback_logerr (status, + "Could not get device buffer frame range\n"); + return status; + } + + if (frameRange.mMinimum > core->frameSizeSetting) { + core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; + dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); + } else if (frameRange.mMaximum < core->frameSizeSetting) { + core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; + dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); + } else { + core->audioDevicePropertyBufferFrameSize = core->frameSizeSetting; + } + + /* set Buffer Frame Size */ + status = coreaudio_set_framesize(core->outputDeviceID, + &core->audioDevicePropertyBufferFrameSize); + if (status == kAudioHardwareBadObjectError) { + return 0; + } + if (status != kAudioHardwareNoError) { + coreaudio_playback_logerr (status, + "Could not set device buffer frame size %" PRIu32 "\n", + (uint32_t)core->audioDevicePropertyBufferFrameSize); + return status; + } + + /* get Buffer Frame Size */ + status = coreaudio_get_framesize(core->outputDeviceID, + &core->audioDevicePropertyBufferFrameSize); + if (status == kAudioHardwareBadObjectError) { + return 0; + } + if (status != kAudioHardwareNoError) { + coreaudio_playback_logerr (status, + "Could not get device buffer frame size\n"); + return status; + } + core->hw.samples = core->bufferCount * core->audioDevicePropertyBufferFrameSize; + + /* set Samplerate */ + status = coreaudio_set_streamformat(core->outputDeviceID, + &streamBasicDescription); + if (status == kAudioHardwareBadObjectError) { + return 0; + } + if (status != kAudioHardwareNoError) { + coreaudio_playback_logerr (status, + "Could not set samplerate %lf\n", + streamBasicDescription.mSampleRate); + core->outputDeviceID = kAudioDeviceUnknown; + return status; + } + + /* + * set Callback. + * + * On macOS 11.3.1, Core Audio calls AudioDeviceIOProc after calling an + * internal function named HALB_Mutex::Lock(), which locks a mutex in + * HALB_IOThread::Entry(void*). HALB_Mutex::Lock() is also called in + * AudioObjectGetPropertyData, which is called by coreaudio driver. + * Therefore, the specified callback must be designed to avoid a deadlock + * with the callers of AudioObjectGetPropertyData. + */ + core->ioprocid = NULL; + status = AudioDeviceCreateIOProcID(core->outputDeviceID, + audioDeviceIOProc, + &core->hw, + &core->ioprocid); + if (status == kAudioHardwareBadDeviceError) { + return 0; + } + if (status != kAudioHardwareNoError || core->ioprocid == NULL) { + coreaudio_playback_logerr (status, "Could not set IOProc\n"); + core->outputDeviceID = kAudioDeviceUnknown; + return status; + } + + return 0; +} + +static void fini_out_device(coreaudioVoiceOut *core) +{ + OSStatus status; + UInt32 isrunning; + + /* stop playback */ + status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning); + if (status != kAudioHardwareBadObjectError) { + if (status != kAudioHardwareNoError) { + coreaudio_logerr(status, + "Could not determine whether Device is playing\n"); + } + + if (isrunning) { + status = AudioDeviceStop(core->outputDeviceID, core->ioprocid); + if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { + coreaudio_logerr(status, "Could not stop playback\n"); + } + } + } + + /* remove callback */ + status = AudioDeviceDestroyIOProcID(core->outputDeviceID, + core->ioprocid); + if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { + coreaudio_logerr(status, "Could not remove IOProc\n"); + } + core->outputDeviceID = kAudioDeviceUnknown; +} + +static void update_device_playback_state(coreaudioVoiceOut *core) +{ + OSStatus status; + UInt32 isrunning; + + status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning); + if (status != kAudioHardwareNoError) { + if (status != kAudioHardwareBadObjectError) { + coreaudio_logerr(status, + "Could not determine whether Device is playing\n"); + } + + return; + } + + if (core->enabled) { + /* start playback */ + if (!isrunning) { + status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); + if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { + coreaudio_logerr (status, "Could not resume playback\n"); + } + } + } else { + /* stop playback */ + if (isrunning) { + status = AudioDeviceStop(core->outputDeviceID, + core->ioprocid); + if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { + coreaudio_logerr(status, "Could not pause playback\n"); + } + } + } +} + +/* called without iothread lock. */ +static OSStatus handle_voice_change( + AudioObjectID in_object_id, + UInt32 in_number_addresses, + const AudioObjectPropertyAddress *in_addresses, + void *in_client_data) +{ + coreaudioVoiceOut *core = in_client_data; + + qemu_mutex_lock_iothread(); + + if (core->outputDeviceID) { + fini_out_device(core); + } + + if (!init_out_device(core)) { + update_device_playback_state(core); + } + + qemu_mutex_unlock_iothread(); + return 0; +} + +static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, + void *drv_opaque) +{ + OSStatus status; + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + int err; + Audiodev *dev = drv_opaque; + AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out; + struct audsettings obt_as; + + /* create mutex */ + err = pthread_mutex_init(&core->buf_mutex, NULL); + if (err) { + dolog("Could not create mutex\nReason: %s\n", strerror (err)); + return -1; + } + + obt_as = *as; + as = &obt_as; + as->fmt = AUDIO_FORMAT_F32; + audio_pcm_init_info (&hw->info, as); + + core->frameSizeSetting = audio_buffer_frames( + qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610); + + core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4; + + status = AudioObjectAddPropertyListener(kAudioObjectSystemObject, + &voice_addr, handle_voice_change, + core); + if (status != kAudioHardwareNoError) { + coreaudio_playback_logerr (status, + "Could not listen to voice property change\n"); + return -1; + } + + if (init_out_device(core)) { + status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, + &voice_addr, + handle_voice_change, + core); + if (status != kAudioHardwareNoError) { + coreaudio_playback_logerr(status, + "Could not remove voice property change listener\n"); + } + + return -1; + } + + return 0; +} + +static void coreaudio_fini_out (HWVoiceOut *hw) +{ + OSStatus status; + int err; + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + + status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, + &voice_addr, + handle_voice_change, + core); + if (status != kAudioHardwareNoError) { + coreaudio_logerr(status, "Could not remove voice property change listener\n"); + } + + fini_out_device(core); + + /* destroy mutex */ + err = pthread_mutex_destroy(&core->buf_mutex); + if (err) { + dolog("Could not destroy mutex\nReason: %s\n", strerror (err)); + } +} + +static void coreaudio_enable_out(HWVoiceOut *hw, bool enable) +{ + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + + core->enabled = enable; + update_device_playback_state(core); +} + +static void *coreaudio_audio_init(Audiodev *dev) +{ + return dev; +} + +static void coreaudio_audio_fini (void *opaque) +{ +} + +static struct audio_pcm_ops coreaudio_pcm_ops = { + .init_out = coreaudio_init_out, + .fini_out = coreaudio_fini_out, + /* wrapper for audio_generic_write */ + .write = coreaudio_write, + /* wrapper for audio_generic_buffer_get_free */ + .buffer_get_free = coreaudio_buffer_get_free, + /* wrapper for audio_generic_get_buffer_out */ + .get_buffer_out = coreaudio_get_buffer_out, + /* wrapper for audio_generic_put_buffer_out */ + .put_buffer_out = coreaudio_put_buffer_out, + .enable_out = coreaudio_enable_out +}; + +static struct audio_driver coreaudio_audio_driver = { + .name = "coreaudio", + .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html", + .init = coreaudio_audio_init, + .fini = coreaudio_audio_fini, + .pcm_ops = &coreaudio_pcm_ops, + .can_be_default = 1, + .max_voices_out = 1, + .max_voices_in = 0, + .voice_size_out = sizeof (coreaudioVoiceOut), + .voice_size_in = 0 +}; + +static void register_audio_coreaudio(void) +{ + audio_driver_register(&coreaudio_audio_driver); +} +type_init(register_audio_coreaudio); diff --git a/audio/meson.build b/audio/meson.build index d9b2955..94dab16 100644 --- a/audio/meson.build +++ b/audio/meson.build @@ -7,7 +7,7 @@ softmmu_ss.add(files( 'wavcapture.c', )) -softmmu_ss.add(when: coreaudio, if_true: files('coreaudio.c')) +softmmu_ss.add(when: coreaudio, if_true: files('coreaudio.m')) softmmu_ss.add(when: dsound, if_true: files('dsoundaudio.c', 'audio_win_int.c')) audio_modules = {} -- cgit v1.1 From 98f5ebfd21b144fb70b02df3e419dec0ee2a52a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 15 Feb 2022 12:34:58 +0100 Subject: osdep: Avoid using Clang-specific __builtin_available() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the Clang specific __builtin_available() to allow building with GCC, otherwise we get: include/qemu/osdep.h: In function 'qemu_thread_jit_write': include/qemu/osdep.h:787:9: warning: implicit declaration of function '__builtin_available'; did you mean '__builtin_scalbl'? [-Wimplicit-function-declaration] 787 | if (__builtin_available(macOS 11.0, *)) { | ^~~~~~~~~~~~~~~~~~~ | __builtin_scalbl include/qemu/osdep.h:787:9: warning: nested extern declaration of '__builtin_available' [-Wnested-externs] include/qemu/osdep.h:787:29: error: 'macOS' undeclared (first use in this function) 787 | if (__builtin_available(macOS 11.0, *)) { | ^~~~~ include/qemu/osdep.h:787:29: note: each undeclared identifier is reported only once for each function it appears in include/qemu/osdep.h:787:34: error: expected ')' before numeric constant 787 | if (__builtin_available(macOS 11.0, *)) { | ~ ^~~~~ | ) Beside, on macOS Catalina we get 2254 times: include/qemu/osdep.h:780:5: warning: 'pthread_jit_write_protect_np' is only available on macOS 11.0 or newer [-Wunguarded-availability-new] pthread_jit_write_protect_np(true); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ Fix by using a stricker toolchain version low range, replacing MAC_OS_X_VERSION_MAX_ALLOWED by MAC_OS_X_VERSION_MIN_REQUIRED. Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Signed-off-by: Philippe Mathieu-Daudé --- include/qemu/osdep.h | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index c9ec783..322103a 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -624,19 +624,15 @@ size_t qemu_get_host_physmem(void); * for the current thread. */ #if defined(MAC_OS_VERSION_11_0) && \ - MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_11_0 + MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 static inline void qemu_thread_jit_execute(void) { - if (__builtin_available(macOS 11.0, *)) { - pthread_jit_write_protect_np(true); - } + pthread_jit_write_protect_np(true); } static inline void qemu_thread_jit_write(void) { - if (__builtin_available(macOS 11.0, *)) { - pthread_jit_write_protect_np(false); - } + pthread_jit_write_protect_np(false); } #else static inline void qemu_thread_jit_write(void) {} -- cgit v1.1 From 30cfa50da53f60283e2e7f3bf0856189a06bb952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 15 Feb 2022 17:25:03 +0100 Subject: meson: Resolve the entitlement.sh script once for good MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 235b523dba ("meson: Use find_program() to resolve the entitlement.sh script") didn't correctly fixed the issue, as the script is still resolved for each target. Move the check earlier, before processing each target. Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Signed-off-by: Philippe Mathieu-Daudé --- meson.build | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 351f9f4..6e8fa4a 100644 --- a/meson.build +++ b/meson.build @@ -3073,6 +3073,10 @@ common_all = static_library('common', feature_to_c = find_program('scripts/feature_to_c.sh') +if targetos == 'darwin' + entitlement = find_program('scripts/entitlement.sh') +endif + emulators = {} foreach target : target_dirs config_target = config_target_mak[target] @@ -3230,7 +3234,6 @@ foreach target : target_dirs install_input += meson.current_source_dir() / entitlements endif - entitlement = find_program('scripts/entitlement.sh') emulators += {exe['name'] : custom_target(exe['name'], input: build_input, output: exe['name'], -- cgit v1.1 From cf3ab8fe9e2360eace4c84b6779e0ab69516ae7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 15 Feb 2022 16:14:35 +0100 Subject: meson: Log QEMU_CXXFLAGS content in summary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Signed-off-by: Philippe Mathieu-Daudé --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 6e8fa4a..5fed238 100644 --- a/meson.build +++ b/meson.build @@ -3480,6 +3480,7 @@ if link_args.length() > 0 summary_info += {'LDFLAGS': ' '.join(link_args)} endif summary_info += {'QEMU_CFLAGS': config_host['QEMU_CFLAGS']} +summary_info += {'QEMU_CXXFLAGS': config_host['QEMU_CXXFLAGS']} summary_info += {'QEMU_LDFLAGS': config_host['QEMU_LDFLAGS']} summary_info += {'profiler': get_option('profiler')} summary_info += {'link-time optimization (LTO)': get_option('b_lto')} -- cgit v1.1 From 4cb37d1192e112159e3f8cb13de98a09fef502fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 15 Feb 2022 16:15:13 +0100 Subject: configure: Pass filtered QEMU_OBJCFLAGS to meson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Filter unsupported Objective-C options, to avoid 'unknown-warning-option' warnings when using Clang: [34/373] Compiling Objective-C object libcommon.fa.p/audio_coreaudio.m.o warning: unknown warning option '-Wold-style-declaration'; did you mean '-Wout-of-line-declaration'? [-Wunknown-warning-option] warning: unknown warning option '-Wimplicit-fallthrough=2'; did you mean '-Wimplicit-fallthrough'? [-Wunknown-warning-option] 2 warnings generated. Acked-by: Paolo Bonzini Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Signed-off-by: Philippe Mathieu-Daudé --- configure | 23 +++++++++++++++++++++++ meson.build | 6 +++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/configure b/configure index 2bf40d9..cd4946b 100755 --- a/configure +++ b/configure @@ -77,6 +77,7 @@ TMPB="qemu-conf" TMPC="${TMPDIR1}/${TMPB}.c" TMPO="${TMPDIR1}/${TMPB}.o" TMPCXX="${TMPDIR1}/${TMPB}.cxx" +TMPM="${TMPDIR1}/${TMPB}.m" TMPE="${TMPDIR1}/${TMPB}.exe" rm -f config.log @@ -148,6 +149,10 @@ do_cxx() { do_compiler "$cxx" $CPU_CFLAGS "$@" } +do_objc() { + do_compiler "$objcc" $CPU_CFLAGS "$@" +} + # Append $2 to the variable named $1, with space separation add_to() { eval $1=\${$1:+\"\$$1 \"}\$2 @@ -1445,10 +1450,27 @@ cc_has_warning_flag() { compile_prog "-Werror $optflag" "" } +objcc_has_warning_flag() { + cat > $TMPM <> $config_host_mak echo "CFLAGS_NOPIE=$CFLAGS_NOPIE" >> $config_host_mak echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak echo "QEMU_CXXFLAGS=$QEMU_CXXFLAGS" >> $config_host_mak +echo "QEMU_OBJCFLAGS=$QEMU_OBJCFLAGS" >> $config_host_mak echo "GLIB_CFLAGS=$glib_cflags" >> $config_host_mak echo "GLIB_LIBS=$glib_libs" >> $config_host_mak echo "GLIB_VERSION=$(pkg-config --modversion glib-2.0)" >> $config_host_mak diff --git a/meson.build b/meson.build index 5fed238..cb24d30 100644 --- a/meson.build +++ b/meson.build @@ -173,11 +173,13 @@ endif qemu_cflags = config_host['QEMU_CFLAGS'].split() qemu_cxxflags = config_host['QEMU_CXXFLAGS'].split() +qemu_objcflags = config_host['QEMU_OBJCFLAGS'].split() qemu_ldflags = config_host['QEMU_LDFLAGS'].split() if get_option('gprof') qemu_cflags += ['-p'] qemu_cxxflags += ['-p'] + qemu_objcflags += ['-p'] qemu_ldflags += ['-p'] endif @@ -216,8 +218,9 @@ if get_option('fuzzing') endif endif -add_global_arguments(qemu_cflags, native: false, language: ['c', 'objc']) +add_global_arguments(qemu_cflags, native: false, language: ['c']) add_global_arguments(qemu_cxxflags, native: false, language: ['cpp']) +add_global_arguments(qemu_objcflags, native: false, language: ['objc']) add_global_link_arguments(qemu_ldflags, native: false, language: ['c', 'cpp', 'objc']) if targetos == 'linux' @@ -3481,6 +3484,7 @@ if link_args.length() > 0 endif summary_info += {'QEMU_CFLAGS': config_host['QEMU_CFLAGS']} summary_info += {'QEMU_CXXFLAGS': config_host['QEMU_CXXFLAGS']} +summary_info += {'QEMU_OBJCFLAGS': config_host['QEMU_OBJCFLAGS']} summary_info += {'QEMU_LDFLAGS': config_host['QEMU_LDFLAGS']} summary_info += {'profiler': get_option('profiler')} summary_info += {'link-time optimization (LTO)': get_option('b_lto')} -- cgit v1.1 From 9459262dc4218110f4d3c5dce0b4fe43e1faffdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 15 Feb 2022 16:21:14 +0100 Subject: ui/cocoa: Constify qkeycode translation arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported-by: Peter Maydell Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Signed-off-by: Philippe Mathieu-Daudé --- ui/cocoa.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/cocoa.m b/ui/cocoa.m index c881498..a66ba3d 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -671,7 +671,7 @@ QemuCocoaView *cocoaView; /* translates Macintosh keycodes to QEMU's keysym */ - int without_control_translation[] = { + static const int without_control_translation[] = { [0 ... 0xff] = 0, // invalid key [kVK_UpArrow] = QEMU_KEY_UP, @@ -686,7 +686,7 @@ QemuCocoaView *cocoaView; [kVK_Delete] = QEMU_KEY_BACKSPACE, }; - int with_control_translation[] = { + static const int with_control_translation[] = { [0 ... 0xff] = 0, // invalid key [kVK_UpArrow] = QEMU_KEY_CTRL_UP, -- cgit v1.1 From 48941a52c2c3350b0c3dcdc1aba702def91d19a8 Mon Sep 17 00:00:00 2001 From: Carwyn Ellis Date: Sun, 2 Jan 2022 17:41:52 +0000 Subject: ui/cocoa: add option to disable left-command forwarding to guest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When switching between guest and host on a Mac using command-tab the command key is sent to the guest which can trigger functionality in the guest OS. Specifying left-command-key=off disables forwarding this key to the guest. Defaults to enabled. Also updated the cocoa display documentation to reference the new left-command-key option along with the existing show-cursor option. Signed-off-by: Carwyn Ellis Reviewed-by: Akihiko Odaki [PMD: Set QAPI structure @since tag to 7.0] Signed-off-by: Philippe Mathieu-Daudé --- qapi/ui.json | 18 ++++++++++++++++++ qemu-options.hx | 12 ++++++++++++ ui/cocoa.m | 8 +++++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/qapi/ui.json b/qapi/ui.json index 4a13f88..4dea35a 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -1261,6 +1261,23 @@ 'data' : { '*charset' : 'str' } } ## +# @DisplayCocoa: +# +# Cocoa display options. +# +# @left-command-key: Enable/disable forwarding of left command key to +# guest. Allows command-tab window switching on the +# host without sending this key to the guest when +# "off". Defaults to "on" +# +# Since: 7.0 +## +{ 'struct': 'DisplayCocoa', + 'data': { + '*left-command-key': 'bool' + } } + +## # @DisplayType: # # Display (user interface) type. @@ -1338,6 +1355,7 @@ 'discriminator' : 'type', 'data' : { 'gtk': { 'type': 'DisplayGTK', 'if': 'CONFIG_GTK' }, + 'cocoa': { 'type': 'DisplayCocoa', 'if': 'CONFIG_COCOA' }, 'curses': { 'type': 'DisplayCurses', 'if': 'CONFIG_CURSES' }, 'egl-headless': { 'type': 'DisplayEGLHeadless', 'if': { 'all': ['CONFIG_OPENGL', 'CONFIG_GBM'] } }, diff --git a/qemu-options.hx b/qemu-options.hx index 5ce0ada..f464b2f 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1923,6 +1923,9 @@ DEF("display", HAS_ARG, QEMU_OPTION_display, "-display dbus[,addr=]\n" " [,gl=on|core|es|off][,rendernode=]\n" #endif +#if defined(CONFIG_COCOA) + "-display cocoa[,show-cursor=on|off][,left-command-key=on|off]\n" +#endif "-display none\n" " select display backend type\n" " The default display is equivalent to\n " @@ -2009,6 +2012,15 @@ SRST ``charset=CP850`` for IBM CP850 encoding. The default is ``CP437``. + ``cocoa`` + Display video output in a Cocoa window. Mac only. This interface + provides drop-down menus and other UI elements to configure and + control the VM during runtime. Valid parameters are: + + ``show-cursor=on|off`` : Force showing the mouse cursor + + ``left-command-key=on|off`` : Disable forwarding left command key to host + ``egl-headless[,rendernode=]`` Offload all OpenGL operations to a local DRI device. For any graphical display, this display needs to be paired with either diff --git a/ui/cocoa.m b/ui/cocoa.m index a66ba3d..1aa51c4 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -95,6 +95,7 @@ static DisplayChangeListener dcl = { }; static int last_buttons; static int cursor_hide = 1; +static int left_command_key_enabled = 1; static int gArgc; static char **gArgv; @@ -853,7 +854,8 @@ QemuCocoaView *cocoaView; /* Don't pass command key changes to guest unless mouse is grabbed */ case kVK_Command: if (isMouseGrabbed && - !!(modifiers & NSEventModifierFlagCommand)) { + !!(modifiers & NSEventModifierFlagCommand) && + left_command_key_enabled) { [self toggleKey:Q_KEY_CODE_META_L]; } break; @@ -2002,6 +2004,10 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) cursor_hide = 0; } + if (opts->u.cocoa.has_left_command_key && !opts->u.cocoa.left_command_key) { + left_command_key_enabled = 0; + } + // register vga output callbacks register_displaychangelistener(&dcl); -- cgit v1.1 From 69221df8cde644ce75053986b712c40a2a83966a Mon Sep 17 00:00:00 2001 From: Carwyn Ellis Date: Sun, 2 Jan 2022 17:41:53 +0000 Subject: ui/cocoa: release mouse when user switches away from QEMU window MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This resolves an issue where using command-tab to switch between QEMU and other windows on the host can leave the mouse pointer visible. By releasing the mouse when the user switches away, the user must left click on the QEMU window when switching back in order to hide the pointer and return control to the guest. This appraoch ensures that the calls to NSCursor hide and unhide are always balanced and thus work correctly when invoked. Signed-off-by: Carwyn Ellis Reviewed-by: Akihiko Odaki Signed-off-by: Philippe Mathieu-Daudé --- ui/cocoa.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/cocoa.m b/ui/cocoa.m index 1aa51c4..d20c74d 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -1261,6 +1261,7 @@ QemuCocoaView *cocoaView; - (void) applicationWillResignActive: (NSNotification *)aNotification { COCOA_DEBUG("QemuCocoaAppController: applicationWillResignActive\n"); + [cocoaView ungrabMouse]; [cocoaView raiseAllKeys]; } @@ -2000,6 +2001,7 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) [(QemuCocoaAppController *)[[NSApplication sharedApplication] delegate] toggleFullScreen: nil]; }); } + if (opts->has_show_cursor && opts->show_cursor) { cursor_hide = 0; } -- cgit v1.1 From f844cdb997144a3fd83d7d18e32365fc6a1a8a1f Mon Sep 17 00:00:00 2001 From: Gustavo Noronha Silva Date: Sun, 6 Mar 2022 21:11:18 +0900 Subject: ui/cocoa: capture all keys and combos when mouse is grabbed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Applications such as Gnome may use Alt-Tab and Super-Tab for different purposes, some use Ctrl-arrows so we want to allow qemu to handle everything when it captures the mouse/keyboard. However, Mac OS handles some combos like Command-Tab and Ctrl-arrows at an earlier part of the event handling chain, not letting qemu see it. We add a global Event Tap that allows qemu to see all events when the mouse is grabbed. Note that this requires additional permissions. See: https://developer.apple.com/documentation/coregraphics/1454426-cgeventtapcreate?language=objc#discussion https://support.apple.com/en-in/guide/mac-help/mh32356/mac Acked-by: Markus Armbruster Signed-off-by: Gustavo Noronha Silva Message-Id: <20210713213200.2547-2-gustavo@noronha.dev.br> Signed-off-by: Akihiko Odaki Message-Id: <20220306121119.45631-2-akihiko.odaki@gmail.com> Reviewed-by: Will Cohen Signed-off-by: Philippe Mathieu-Daudé --- qapi/ui.json | 8 ++++++- qemu-options.hx | 3 +++ ui/cocoa.m | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/qapi/ui.json b/qapi/ui.json index 4dea35a..1d60d5f 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -1270,11 +1270,17 @@ # host without sending this key to the guest when # "off". Defaults to "on" # +# @full-grab: Capture all key presses, including system combos. This +# requires accessibility permissions, since it performs +# a global grab on key events. (default: off) +# See https://support.apple.com/en-in/guide/mac-help/mh32356/mac +# # Since: 7.0 ## { 'struct': 'DisplayCocoa', 'data': { - '*left-command-key': 'bool' + '*left-command-key': 'bool', + '*full-grab': 'bool' } } ## diff --git a/qemu-options.hx b/qemu-options.hx index f464b2f..4395378 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1916,6 +1916,9 @@ DEF("display", HAS_ARG, QEMU_OPTION_display, #if defined(CONFIG_CURSES) "-display curses[,charset=]\n" #endif +#if defined(CONFIG_COCOA) + "-display cocoa[,full_grab=on|off]\n" +#endif #if defined(CONFIG_OPENGL) "-display egl-headless[,rendernode=]\n" #endif diff --git a/ui/cocoa.m b/ui/cocoa.m index d20c74d..b1250b7 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -309,11 +309,13 @@ static void handleAnyDeviceErrors(Error * err) BOOL isMouseGrabbed; BOOL isFullscreen; BOOL isAbsoluteEnabled; + CFMachPortRef eventsTap; } - (void) switchSurface:(pixman_image_t *)image; - (void) grabMouse; - (void) ungrabMouse; - (void) toggleFullScreen:(id)sender; +- (void) setFullGrab:(id)sender; - (void) handleMonitorInput:(NSEvent *)event; - (bool) handleEvent:(NSEvent *)event; - (bool) handleEventLocked:(NSEvent *)event; @@ -336,6 +338,19 @@ static void handleAnyDeviceErrors(Error * err) QemuCocoaView *cocoaView; +static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEventRef cgEvent, void *userInfo) +{ + QemuCocoaView *cocoaView = userInfo; + NSEvent *event = [NSEvent eventWithCGEvent:cgEvent]; + if ([cocoaView isMouseGrabbed] && [cocoaView handleEvent:event]) { + COCOA_DEBUG("Global events tap: qemu handled the event, capturing!\n"); + return NULL; + } + COCOA_DEBUG("Global events tap: qemu did not handle the event, letting it through...\n"); + + return cgEvent; +} + @implementation QemuCocoaView - (id)initWithFrame:(NSRect)frameRect { @@ -361,6 +376,11 @@ QemuCocoaView *cocoaView; } qkbd_state_free(kbd); + + if (eventsTap) { + CFRelease(eventsTap); + } + [super dealloc]; } @@ -655,6 +675,36 @@ QemuCocoaView *cocoaView; } } +- (void) setFullGrab:(id)sender +{ + COCOA_DEBUG("QemuCocoaView: setFullGrab\n"); + + CGEventMask mask = CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventKeyUp) | CGEventMaskBit(kCGEventFlagsChanged); + eventsTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, + mask, handleTapEvent, self); + if (!eventsTap) { + warn_report("Could not create event tap, system key combos will not be captured.\n"); + return; + } else { + COCOA_DEBUG("Global events tap created! Will capture system key combos.\n"); + } + + CFRunLoopRef runLoop = CFRunLoopGetCurrent(); + if (!runLoop) { + warn_report("Could not obtain current CF RunLoop, system key combos will not be captured.\n"); + return; + } + + CFRunLoopSourceRef tapEventsSrc = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventsTap, 0); + if (!tapEventsSrc ) { + warn_report("Could not obtain current CF RunLoop, system key combos will not be captured.\n"); + return; + } + + CFRunLoopAddSource(runLoop, tapEventsSrc, kCFRunLoopDefaultMode); + CFRelease(tapEventsSrc); +} + - (void) toggleKey: (int)keycode { qkbd_state_key_event(kbd, keycode, !qkbd_state_key_get(kbd, keycode)); } @@ -1281,6 +1331,13 @@ QemuCocoaView *cocoaView; [cocoaView toggleFullScreen:sender]; } +- (void) setFullGrab:(id)sender +{ + COCOA_DEBUG("QemuCocoaAppController: setFullGrab\n"); + + [cocoaView setFullGrab:sender]; +} + /* Tries to find then open the specified filename */ - (void) openDocumentation: (NSString *) filename { @@ -1994,11 +2051,17 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) qemu_sem_wait(&app_started_sem); COCOA_DEBUG("cocoa_display_init: app start completed\n"); + QemuCocoaAppController *controller = (QemuCocoaAppController *)[[NSApplication sharedApplication] delegate]; /* if fullscreen mode is to be used */ if (opts->has_full_screen && opts->full_screen) { dispatch_async(dispatch_get_main_queue(), ^{ [NSApp activateIgnoringOtherApps: YES]; - [(QemuCocoaAppController *)[[NSApplication sharedApplication] delegate] toggleFullScreen: nil]; + [controller toggleFullScreen: nil]; + }); + } + if (opts->u.cocoa.has_full_grab && opts->u.cocoa.full_grab) { + dispatch_async(dispatch_get_main_queue(), ^{ + [controller setFullGrab: nil]; }); } -- cgit v1.1 From 4797adce5f0b7542c5aa4a83bdce7f0e63d6deae Mon Sep 17 00:00:00 2001 From: Gustavo Noronha Silva Date: Sun, 6 Mar 2022 21:11:19 +0900 Subject: ui/cocoa: add option to swap Option and Command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Mac OS X the Option key maps to Alt and Command to Super/Meta. This change swaps them around so that Alt is the key closer to the space bar and Meta/Super is between Control and Alt, like on non-Mac keyboards. It is a cocoa display option, disabled by default. Acked-by: Markus Armbruster Signed-off-by: Gustavo Noronha Silva Message-Id: <20210713213200.2547-3-gustavo@noronha.dev.br> Signed-off-by: Akihiko Odaki Message-Id: <20220306121119.45631-3-akihiko.odaki@gmail.com> Reviewed-by: Will Cohen Signed-off-by: Philippe Mathieu-Daudé --- qapi/ui.json | 7 ++++++- qemu-options.hx | 2 +- ui/cocoa.m | 49 +++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/qapi/ui.json b/qapi/ui.json index 1d60d5f..664da9e 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -1275,12 +1275,17 @@ # a global grab on key events. (default: off) # See https://support.apple.com/en-in/guide/mac-help/mh32356/mac # +# @swap-opt-cmd: Swap the Option and Command keys so that their key codes match +# their position on non-Mac keyboards and you can use Meta/Super +# and Alt where you expect them. (default: off) +# # Since: 7.0 ## { 'struct': 'DisplayCocoa', 'data': { '*left-command-key': 'bool', - '*full-grab': 'bool' + '*full-grab': 'bool', + '*swap-opt-cmd': 'bool' } } ## diff --git a/qemu-options.hx b/qemu-options.hx index 4395378..58f2f76 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1917,7 +1917,7 @@ DEF("display", HAS_ARG, QEMU_OPTION_display, "-display curses[,charset=]\n" #endif #if defined(CONFIG_COCOA) - "-display cocoa[,full_grab=on|off]\n" + "-display cocoa[,full-grab=on|off][,swap-opt-cmd=on|off]\n" #endif #if defined(CONFIG_OPENGL) "-display egl-headless[,rendernode=]\n" diff --git a/ui/cocoa.m b/ui/cocoa.m index b1250b7..cb6e7c4 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -96,6 +96,7 @@ static DisplayChangeListener dcl = { static int last_buttons; static int cursor_hide = 1; static int left_command_key_enabled = 1; +static bool swap_opt_cmd; static int gArgc; static char **gArgv; @@ -854,12 +855,22 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven qkbd_state_key_event(kbd, Q_KEY_CODE_CTRL_R, false); } if (!(modifiers & NSEventModifierFlagOption)) { - qkbd_state_key_event(kbd, Q_KEY_CODE_ALT, false); - qkbd_state_key_event(kbd, Q_KEY_CODE_ALT_R, false); + if (swap_opt_cmd) { + qkbd_state_key_event(kbd, Q_KEY_CODE_META_L, false); + qkbd_state_key_event(kbd, Q_KEY_CODE_META_R, false); + } else { + qkbd_state_key_event(kbd, Q_KEY_CODE_ALT, false); + qkbd_state_key_event(kbd, Q_KEY_CODE_ALT_R, false); + } } if (!(modifiers & NSEventModifierFlagCommand)) { - qkbd_state_key_event(kbd, Q_KEY_CODE_META_L, false); - qkbd_state_key_event(kbd, Q_KEY_CODE_META_R, false); + if (swap_opt_cmd) { + qkbd_state_key_event(kbd, Q_KEY_CODE_ALT, false); + qkbd_state_key_event(kbd, Q_KEY_CODE_ALT_R, false); + } else { + qkbd_state_key_event(kbd, Q_KEY_CODE_META_L, false); + qkbd_state_key_event(kbd, Q_KEY_CODE_META_R, false); + } } switch ([event type]) { @@ -891,29 +902,44 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven case kVK_Option: if (!!(modifiers & NSEventModifierFlagOption)) { - [self toggleKey:Q_KEY_CODE_ALT]; + if (swap_opt_cmd) { + [self toggleKey:Q_KEY_CODE_META_L]; + } else { + [self toggleKey:Q_KEY_CODE_ALT]; + } } break; case kVK_RightOption: if (!!(modifiers & NSEventModifierFlagOption)) { - [self toggleKey:Q_KEY_CODE_ALT_R]; + if (swap_opt_cmd) { + [self toggleKey:Q_KEY_CODE_META_R]; + } else { + [self toggleKey:Q_KEY_CODE_ALT_R]; + } } break; /* Don't pass command key changes to guest unless mouse is grabbed */ case kVK_Command: if (isMouseGrabbed && - !!(modifiers & NSEventModifierFlagCommand) && - left_command_key_enabled) { - [self toggleKey:Q_KEY_CODE_META_L]; + !!(modifiers & NSEventModifierFlagCommand)) { + if (swap_opt_cmd) { + [self toggleKey:Q_KEY_CODE_ALT]; + } else { + [self toggleKey:Q_KEY_CODE_META_L]; + } } break; case kVK_RightCommand: if (isMouseGrabbed && !!(modifiers & NSEventModifierFlagCommand)) { - [self toggleKey:Q_KEY_CODE_META_R]; + if (swap_opt_cmd) { + [self toggleKey:Q_KEY_CODE_ALT_R]; + } else { + [self toggleKey:Q_KEY_CODE_META_R]; + } } break; } @@ -2068,6 +2094,9 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) if (opts->has_show_cursor && opts->show_cursor) { cursor_hide = 0; } + if (opts->u.cocoa.has_swap_opt_cmd) { + swap_opt_cmd = opts->u.cocoa.swap_opt_cmd; + } if (opts->u.cocoa.has_left_command_key && !opts->u.cocoa.left_command_key) { left_command_key_enabled = 0; -- cgit v1.1 From c82b7ef16f3efa59e28f821f25a9c084ef84ea9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 14 Mar 2022 18:28:35 +0100 Subject: MAINTAINERS: Volunteer to maintain Darwin-based hosts support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While I am not an experienced Darwin OS user, I now have to use a macOS based workstation and alike CI, meaning I should easily spot regressions and test fixes. I therefore volunteer to collect Darwin related patches and keep QEMU in good state on macOS, and to some extent iOS. Cc: Gerd Hoffmann Cc: Christian Schoenebeck Cc: Akihiko Odaki Cc: Joelle van Dyne Cc: Daniel P. Berrangé Cc: Peter Maydell Acked-by: Gerd Hoffmann Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index f2e9ce1..caea42c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -548,6 +548,12 @@ F: include/*/*win32* X: qga/*win32* F: qemu.nsi +Darwin (macOS, iOS) +M: Philippe Mathieu-Daudé +S: Odd Fixes +F: .gitlab-ci.d/cirrus/macos-* +F: */*.m + Alpha Machines -------------- M: Richard Henderson @@ -2414,6 +2420,7 @@ F: audio/alsaaudio.c Core Audio framework backend M: Gerd Hoffmann +M: Philippe Mathieu-Daudé R: Christian Schoenebeck R: Akihiko Odaki S: Odd Fixes @@ -2671,6 +2678,7 @@ F: util/drm.c Cocoa graphics M: Peter Maydell +M: Philippe Mathieu-Daudé R: Akihiko Odaki S: Odd Fixes F: ui/cocoa.m -- cgit v1.1