diff --git a/hw/audio/Kconfig b/hw/audio/Kconfig index e9c6fed8261eb..196da6c3fe768 100644 --- a/hw/audio/Kconfig +++ b/hw/audio/Kconfig @@ -50,3 +50,6 @@ config CS4231 config MARVELL_88W8618 bool + +config SCREAMER + bool diff --git a/hw/audio/Makefile.objs b/hw/audio/Makefile.objs index 63db383709a16..55906886bc991 100644 --- a/hw/audio/Makefile.objs +++ b/hw/audio/Makefile.objs @@ -15,4 +15,6 @@ common-obj-$(CONFIG_CS4231) += cs4231.o common-obj-$(CONFIG_MARVELL_88W8618) += marvell_88w8618.o common-obj-$(CONFIG_MILKYMIST) += milkymist-ac97.o +common-obj-$(CONFIG_SCREAMER) += screamer.o + common-obj-y += soundhw.o diff --git a/hw/audio/screamer.c b/hw/audio/screamer.c new file mode 100644 index 0000000000000..615a42e1ebbec --- /dev/null +++ b/hw/audio/screamer.c @@ -0,0 +1,397 @@ +/* + * QEMU PowerMac Awacs Screamer device support + * + * Copyright (c) 2016 Mark Cave-Ayland + * + * 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 "audio/audio.h" +#include "hw/hw.h" +#include "hw/irq.h" +#include "hw/ppc/mac.h" +#include "hw/ppc/mac_dbdma.h" +#include "sysemu/sysemu.h" +#include "qemu/cutils.h" +#include "qemu/log.h" +#include "qemu/typedefs.h" + +/* debug screamer */ +//#define DEBUG_SCREAMER + +#ifdef DEBUG_SCREAMER +#define SCREAMER_DPRINTF(fmt, ...) \ + do { printf("SCREAMER: " fmt , ## __VA_ARGS__); } while (0) +#else +#define SCREAMER_DPRINTF(fmt, ...) +#endif + +/* chip registers */ +#define SND_CTRL_REG 0x0 +#define CODEC_CTRL_REG 0x1 +#define CODEC_STAT_REG 0x2 +#define CLIP_CNT_REG 0x3 +#define BYTE_SWAP_REG 0x4 +#define FRAME_CNT_REG 0x5 + +#define CODEC_CTRL_MASKECMD (0x1 << 24) +#define CODEC_CTRL1_RECALIBRATE 0x4 + +#define CODEC_STAT_MANUFACTURER_CRYSTAL 0x100 +#define CODEC_STAT_AWACS_REVISION 0x3000 +#define CODEC_STAT_MASK_VALID (0x1 << 22) + +/* Audio */ +static const char *s_spk = "screamer"; + +static void pmac_screamer_tx_transfer(DBDMA_io *io) +{ + ScreamerState *s = io->opaque; + + SCREAMER_DPRINTF("DMA TX transfer: addr %" HWADDR_PRIx + " len: %x bpos: %d\n", io->addr, io->len, s->bpos); + + dma_memory_read(&address_space_memory, io->addr, &s->buf[s->bpos], io->len); + + s->bpos += io->len; + + /* Indicate success */ + io->len = 0; + + /* Finish */ + io->dma_end(io); +} + +static void pmac_screamer_tx(DBDMA_io *io) +{ + ScreamerState *s = io->opaque; + + if (s->bpos + io->len > SCREAMER_BUFFER_SIZE) { + /* Not enough space in the buffer, so defer IRQ */ + memcpy(&s->io, io, sizeof(DBDMA_io)); + + SCREAMER_DPRINTF("DMA TX defer interrupt!\n"); + return; + } + + s->io.addr = 0; + s->io.len = 0; + + pmac_screamer_tx_transfer(io); +} + +static void pmac_screamer_tx_flush(DBDMA_io *io) +{ + SCREAMER_DPRINTF("DMA TX flush!\n"); +} + +static void pmac_screamer_rx(DBDMA_io *io) +{ + SCREAMER_DPRINTF("DMA RX transfer: addr %" HWADDR_PRIx + " len: %x\n", io->addr, io->len); + + ScreamerState *s = io->opaque; + DBDMAState *dbs = s->dbdma; + DBDMA_channel *ch = &dbs->channels[0x12]; + + /* FIXME: stop channel after updating with status to stop MacOS 9 freezing */ + ch->regs[DBDMA_STATUS] = 0x0; + + io->dma_end(io); +} + +static void pmac_screamer_rx_flush(DBDMA_io *io) +{ + SCREAMER_DPRINTF("DMA RX flush!\n"); +} + +void macio_screamer_register_dma(ScreamerState *s, void *dbdma, int txchannel, int rxchannel) +{ + s->dbdma = dbdma; + DBDMA_register_channel(dbdma, txchannel, s->dma_tx_irq, + pmac_screamer_tx, pmac_screamer_tx_flush, s); + DBDMA_register_channel(dbdma, rxchannel, s->dma_rx_irq, + pmac_screamer_rx, pmac_screamer_rx_flush, s); +} + +static void screamerspk_callback(void *opaque, int avail) +{ + ScreamerState *s = opaque; + int n, len; + + if (s->bpos) { + if (s->ppos < s->bpos) { + n = MIN(s->bpos - s->ppos, (unsigned int)avail); + SCREAMER_DPRINTF("########### AUDIO WRITE! %d / %d - %d\n", s->ppos, s->bpos, n); + len = AUD_write(s->voice, &s->buf[s->ppos], n); + s->ppos += len; + return; + } + } + + if (s->io.len) { + /* Deferred IRQ */ + s->bpos = 0; + s->ppos = 0; + + SCREAMER_DPRINTF("Processing deferred buffer\n"); + pmac_screamer_tx_transfer(&s->io); + } +} + +static void screamer_update_settings(ScreamerState *s) +{ + struct audsettings as = { s->rate, 2, AUDIO_FORMAT_S16, + s->regs[BYTE_SWAP_REG] ? 0 : 1 }; + + s->voice = AUD_open_out(&s->card, s->voice, s_spk, s, screamerspk_callback, &as); + if (!s->voice) { + AUD_log(s_spk, "Could not open voice\n"); + return; + } + + AUD_set_active_out(s->voice, true); +} + +static void screamer_update_volume(ScreamerState *s) +{ + uint8_t muted = s->codec_ctrl_regs[0x1] & 0x80 ? 1 : 0; + uint8_t att_left = (s->codec_ctrl_regs[0x4] & 0xf); + uint8_t att_right = (s->codec_ctrl_regs[0x4] & 0x3c0) >> 6; + + SCREAMER_DPRINTF("setting mute: %d, attenuation L: %d R: %d\n", + muted, att_left, att_right); + + AUD_set_volume_out(s->voice, muted, (0xf - att_left) << 4, + (0xf - att_right) << 4); +} + +static void screamer_reset(DeviceState *dev) +{ + ScreamerState *s = SCREAMER(dev); + + memset(s->regs, 0, sizeof(s->regs)); + memset(s->codec_ctrl_regs, 0, sizeof(s->codec_ctrl_regs)); + + s->rate = 44100; + s->bpos = 0; + s->ppos = 0; + + screamer_update_settings(s); + + return; +} + +static void screamer_realizefn(DeviceState *dev, Error **errp) +{ + ScreamerState *s = SCREAMER(dev); + + AUD_register_card(s_spk, &s->card); + return; +} + +static void screamer_control_write(ScreamerState *s, uint32_t val) +{ + SCREAMER_DPRINTF("%s: val %" PRId32 "\n", __func__, val); + + /* Basic rate selection */ + switch ((val & 0x700) >> 8) { + case 0x00: + s->rate = 44100; + break; + case 0x1: + s->rate = 29400; + break; + case 0x2: + s->rate = 22050; + break; + case 0x3: + s->rate = 17640; + break; + case 0x4: + s->rate = 14700; + break; + case 0x5: + s->rate = 11025; + break; + case 0x6: + s->rate = 8820; + break; + case 0x7: + s->rate = 7350; + break; + } + + SCREAMER_DPRINTF("basic rate: %d\n", s->rate); + screamer_update_settings(s); + + s->regs[0] = val; +} + +static void screamer_codec_write(ScreamerState *s, hwaddr addr, uint64_t val) +{ + SCREAMER_DPRINTF("%s: addr " TARGET_FMT_plx " val %" PRIx64 "\n", __func__, addr, val); + + switch (addr) { + case 0x1: + /* Clear recalibrate if set */ + val = val & ~CODEC_CTRL1_RECALIBRATE; + + /* Update volume in case mute set */ + screamer_update_volume(s); + break; + + case 0x4: + /* Speaker attenuation */ + screamer_update_volume(s); + break; + } + + s->codec_ctrl_regs[addr] = val; +} + +static uint64_t screamer_read(void *opaque, hwaddr addr, unsigned size) +{ + ScreamerState *s = opaque; + uint32_t val; + + addr = addr >> 4; + switch (addr) { + case SND_CTRL_REG: + val = s->regs[addr]; + break; + case CODEC_CTRL_REG: + val = s->regs[addr] & ~CODEC_CTRL_MASKECMD; + break; + case CODEC_STAT_REG: + if (s->codec_ctrl_regs[7] & 1) { + /* Read back mode */ + val = s->codec_ctrl_regs[(s->codec_ctrl_regs[7] >> 1) & 0xe]; + } else { + /* Return status register */ + val = s->regs[addr] & ~0xff00; + val |= CODEC_STAT_MANUFACTURER_CRYSTAL | CODEC_STAT_AWACS_REVISION | + CODEC_STAT_MASK_VALID; + } + break; + case CLIP_CNT_REG: + case BYTE_SWAP_REG: + case FRAME_CNT_REG: + val = s->regs[addr]; + break; + default: + qemu_log_mask(LOG_UNIMP, + "screamer: Unimplemented register read " + "reg 0x%" HWADDR_PRIx " size 0x%x\n", + addr, size); + val = 0; + break; + } + + SCREAMER_DPRINTF("%s: addr " TARGET_FMT_plx " -> %x\n", __func__, addr, val); + + return val; +} + +static void screamer_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + ScreamerState *s = opaque; + uint32_t codec_addr; + + addr = addr >> 4; + + SCREAMER_DPRINTF("%s: addr " TARGET_FMT_plx " val %" PRIx64 "\n", __func__, addr, val); + + switch (addr) { + case SND_CTRL_REG: + screamer_control_write(s, val & 0xffffffff); + break; + case CODEC_CTRL_REG: + s->regs[addr] = val & 0xffffffff; + codec_addr = (val & 0x7fff) >> 12; + screamer_codec_write(s, codec_addr, val & 0xfff); + break; + case CODEC_STAT_REG: + case CLIP_CNT_REG: + case BYTE_SWAP_REG: + s->regs[addr] = val & 0xffffffff; + break; + default: + qemu_log_mask(LOG_UNIMP, + "screamer: Unimplemented register write " + "reg 0x%" HWADDR_PRIx " size 0x%x value 0x%" PRIx64 "\n", + addr, size, val); + break; + } + + return; +} + +static const MemoryRegionOps screamer_ops = { + .read = screamer_read, + .write = screamer_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static void screamer_initfn(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + ScreamerState *s = SCREAMER(obj); + + memory_region_init_io(&s->mem, obj, &screamer_ops, s, "screamer", 0x1000); + sysbus_init_mmio(d, &s->mem); + sysbus_init_irq(d, &s->irq); + sysbus_init_irq(d, &s->dma_tx_irq); + sysbus_init_irq(d, &s->dma_rx_irq); +} + +static Property screamer_properties[] = { + DEFINE_AUDIO_PROPERTIES(ScreamerState, card), + DEFINE_PROP_END_OF_LIST() +}; + +static void screamer_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = screamer_realizefn; + dc->reset = screamer_reset; + device_class_set_props(dc, screamer_properties); +} + +static const TypeInfo screamer_type_info = { + .name = TYPE_SCREAMER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ScreamerState), + .instance_init = screamer_initfn, + .class_init = screamer_class_init, +}; + +static void screamer_register_types(void) +{ + type_register_static(&screamer_type_info); +} + +type_init(screamer_register_types) diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index 79222192e858c..971924936cc72 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -118,6 +118,15 @@ static void macio_common_realize(PCIDevice *d, Error **errp) memory_region_add_subregion(&s->bar, 0x08000, sysbus_mmio_get_region(sysbus_dev, 0)); + object_property_set_bool(OBJECT(&s->screamer), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_dev = SYS_BUS_DEVICE(&s->screamer); + memory_region_add_subregion(&s->bar, 0x14000, + sysbus_mmio_get_region(sysbus_dev, 0)); + qdev_prop_set_uint32(DEVICE(&s->escc), "disabled", 0); qdev_prop_set_uint32(DEVICE(&s->escc), "frequency", ESCC_CLOCK); qdev_prop_set_uint32(DEVICE(&s->escc), "it_shift", 4); @@ -130,7 +139,6 @@ static void macio_common_realize(PCIDevice *d, Error **errp) error_propagate(errp, err); return; } - macio_bar_setup(s); pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar); } @@ -217,6 +225,13 @@ static void macio_oldworld_realize(PCIDevice *d, Error **errp) error_propagate(errp, err); return; } + + /* Screamer */ + sysbus_dev = SYS_BUS_DEVICE(&s->screamer); + sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, OLDWORLD_SCREAMER_TX_IRQ)); + sysbus_connect_irq(sysbus_dev, 1, qdev_get_gpio_in(pic_dev, OLDWORLD_SCREAMER_TX_DMA_IRQ)); + sysbus_connect_irq(sysbus_dev, 1, qdev_get_gpio_in(pic_dev, OLDWORLD_SCREAMER_RX_IRQ)); + macio_screamer_register_dma(SCREAMER(sysbus_dev), &s->dbdma, 0x10, 0x12); } static void macio_init_ide(MacIOState *s, MACIOIDEState *ide, size_t ide_size, @@ -386,6 +401,13 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) memory_region_add_subregion(&s->bar, 0x16000, sysbus_mmio_get_region(sysbus_dev, 0)); } + + /* Screamer */ + sysbus_dev = SYS_BUS_DEVICE(&s->screamer); + sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, NEWWORLD_SCREAMER_IRQ)); + sysbus_connect_irq(sysbus_dev, 1, qdev_get_gpio_in(pic_dev, NEWWORLD_SCREAMER_DMA_IRQ)); + sysbus_connect_irq(sysbus_dev, 2, qdev_get_gpio_in(pic_dev, NEWWORLD_SCREAMER_RX_IRQ)); + macio_screamer_register_dma(SCREAMER(sysbus_dev), &s->dbdma, 0x10, 0x12); } static void macio_newworld_init(Object *obj) @@ -420,6 +442,9 @@ static void macio_instance_init(Object *obj) TYPE_MAC_DBDMA); macio_init_child_obj(s, "escc", &s->escc, sizeof(s->escc), TYPE_ESCC); + + macio_init_child_obj(s, "screamer", &s->screamer, sizeof(s->screamer), + TYPE_SCREAMER); } static const VMStateDescription vmstate_macio_oldworld = { diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index 354828bf132f9..51bc1a6a90ae2 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -92,6 +92,7 @@ config MAC_OLDWORLD select HEATHROW_PIC select MACIO select FW_CFG_PPC + select SCREAMER config MAC_NEWWORLD bool @@ -104,6 +105,7 @@ config MAC_NEWWORLD select MAC_PMU select UNIN_PCI select FW_CFG_PPC + select SCREAMER config E500 bool diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 6af87d1fa0517..04e498bc57f98 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -32,8 +32,10 @@ #include "hw/sysbus.h" #include "hw/input/adb.h" #include "hw/misc/mos6522.h" +#include "hw/ppc/mac_dbdma.h" #include "hw/pci/pci_host.h" #include "hw/pci-host/uninorth.h" +#include "audio/audio.h" /* SMP is not enabled, for now */ #define MAX_CPUS 1 @@ -56,6 +58,9 @@ #define OLDWORLD_IDE0_DMA_IRQ 0x2 #define OLDWORLD_IDE1_IRQ 0xe #define OLDWORLD_IDE1_DMA_IRQ 0x3 +#define OLDWORLD_SCREAMER_TX_IRQ 0x11 +#define OLDWORLD_SCREAMER_TX_DMA_IRQ 0x08 +#define OLDWORLD_SCREAMER_RX_IRQ 0x09 /* New World IRQs */ #define NEWWORLD_CUDA_IRQ 0x19 @@ -68,6 +73,9 @@ #define NEWWORLD_IDE1_DMA_IRQ 0x3 #define NEWWORLD_EXTING_GPIO1 0x2f #define NEWWORLD_EXTING_GPIO9 0x37 +#define NEWWORLD_SCREAMER_IRQ 0x18 +#define NEWWORLD_SCREAMER_DMA_IRQ 0x09 +#define NEWWORLD_SCREAMER_RX_IRQ 0x0a /* Core99 machine */ #define TYPE_CORE99_MACHINE MACHINE_TYPE_NAME("mac99") @@ -85,6 +93,36 @@ typedef struct Core99MachineState { uint8_t via_config; } Core99MachineState; +/* Screamer */ +#define TYPE_SCREAMER "screamer" +#define SCREAMER(obj) OBJECT_CHECK(ScreamerState, (obj), TYPE_SCREAMER) + +#define SCREAMER_BUFFER_SIZE 0x4000 + +typedef struct ScreamerState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + MemoryRegion mem; + qemu_irq irq; + void *dbdma; + qemu_irq dma_tx_irq; + qemu_irq dma_rx_irq; + + QEMUSoundCard card; + SWVoiceOut *voice; + uint8_t buf[SCREAMER_BUFFER_SIZE]; + uint32_t bpos; + uint32_t ppos; + uint32_t rate; + DBDMA_io io; + + uint32_t regs[6]; + uint32_t codec_ctrl_regs[8]; +} ScreamerState; + +void macio_screamer_register_dma(ScreamerState *s, void *dbdma, int txchannel, int rxchannel); + /* Grackle PCI */ #define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" diff --git a/include/hw/misc/macio/macio.h b/include/hw/misc/macio/macio.h index 070a694eb536a..5ade1ea7655fb 100644 --- a/include/hw/misc/macio/macio.h +++ b/include/hw/misc/macio/macio.h @@ -86,6 +86,7 @@ typedef struct MacIOState { PMUState pmu; DBDMAState dbdma; ESCCState escc; + ScreamerState screamer; uint64_t frequency; } MacIOState; @@ -116,6 +117,7 @@ typedef struct NewWorldMacIOState { bool has_pmu; bool has_adb; OpenPICState *pic; + MACIOIDEState ide[2]; MacIOGPIOState gpio; } NewWorldMacIOState; diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc index 1c9ab09af72da..fa931871a81f7 100644 Binary files a/pc-bios/openbios-ppc and b/pc-bios/openbios-ppc differ diff --git a/ui/cocoa.m b/ui/cocoa.m index fbb5b1b45f813..758f5097db35e 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -321,6 +321,12 @@ static void handleAnyDeviceErrors(Error * err) } } +/* Name for the normal window */ +static NSString *normalWindowName(void) { + return qemu_name ? [NSString stringWithFormat:@"QEMU %s", qemu_name] : @"QEMU"; +} + + /* ------------------------------------------------------ QemuCocoaView @@ -404,6 +410,7 @@ - (BOOL) isOpaque return YES; } + - (BOOL) screenContainsPoint:(NSPoint) p { return (p.x > -1 && p.x < screen.width && p.y > -1 && p.y < screen.height); @@ -569,7 +576,6 @@ - (void) switchSurface:(pixman_image_t *)image */ bool isResize = (w != screen.width || h != screen.height || cdx == 0.0); - int oldh = screen.height; if (isResize) { // Resize before we trigger the redraw, or we'll redraw at the wrong size COCOA_DEBUG("switchSurface: new size %d x %d\n", w, h); @@ -592,17 +598,45 @@ - (void) switchSurface:(pixman_image_t *)image pixman_image = image; dataProviderRef = CGDataProviderCreateWithData(NULL, pixman_image_get_data(image), w * 4 * h, NULL); - // update windows if (isFullscreen) { [[fullScreenWindow contentView] setFrame:[[NSScreen mainScreen] frame]]; - [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + oldh, w, h + [normalWindow frame].size.height - oldh) display:NO animate:NO]; } else { - if (qemu_name) - [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]]; - [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + oldh, w, h + [normalWindow frame].size.height - oldh) display:YES animate:NO]; + [self updateWindowFrameForWidth:w height:h isResize:isResize]; + [normalWindow setTitle:normalWindowName()]; } +} - if (isResize) { + +/* Key to use for saving/restoring the window position */ +- (NSString*) windowPositionKey { + return [NSString stringWithFormat: @"Position %@", normalWindowName()]; +} + +/* Store the normal window position in the user defaults. */ +- (void) saveWindowPosition { + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; + NSPoint position = [normalWindow contentRectForFrameRect: [normalWindow frame]].origin; + NSArray* value = @[@(position.x), @(position.y)]; + [defaults setObject:value forKey: [self windowPositionKey]]; +} + +/* Update size of the normal window, keeping the origin constant if the user has positioned it */ +- (void) updateWindowFrameForWidth:(CGFloat)width height:(CGFloat)height isResize:(BOOL)isResize { + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; + NSArray* value = [defaults arrayForKey: [self windowPositionKey]]; + BOOL gotOrigin = [value count] == 2; + + NSRect contentFrame = [normalWindow contentRectForFrameRect: [normalWindow frame]]; + contentFrame.size = NSMakeSize(width, height); + if (gotOrigin) { + // restore window origin if it was saved previously + contentFrame.origin = NSMakePoint([[value objectAtIndex:0] doubleValue], [[value objectAtIndex:1] doubleValue]); + } + NSRect windowFrame = [normalWindow frameRectForContentRect: contentFrame]; + [normalWindow setFrame:windowFrame display:!isFullscreen animate:NO]; + + if (isResize && !gotOrigin) { + // if we've no saved origin, center the window [normalWindow center]; } } @@ -1006,10 +1040,7 @@ - (void) grabMouse COCOA_DEBUG("QemuCocoaView: grabMouse\n"); if (!isFullscreen) { - if (qemu_name) - [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s - (Press ctrl + alt + g to release Mouse)", qemu_name]]; - else - [normalWindow setTitle:@"QEMU - (Press ctrl + alt + g to release Mouse)"]; + [normalWindow setTitle:[NSString stringWithFormat:@"%@ - (Press ctrl + alt + g to release Mouse)", normalWindowName() ]]; } [self hideCursor]; if (!isAbsoluteEnabled) { @@ -1024,10 +1055,7 @@ - (void) ungrabMouse COCOA_DEBUG("QemuCocoaView: ungrabMouse\n"); if (!isFullscreen) { - if (qemu_name) - [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]]; - else - [normalWindow setTitle:@"QEMU"]; + [normalWindow setTitle:normalWindowName()]; } [self unhideCursor]; if (isMouseDeassociated) { @@ -1109,14 +1137,16 @@ - (id) init if (self) { // create a view and add it to the window - cocoaView = [[QemuCocoaView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 640.0, 480.0)]; + NSRect frame = NSMakeRect(0.0, 0.0, 640.0, 480.0); + cocoaView = [[QemuCocoaView alloc] initWithFrame:frame]; if(!cocoaView) { fprintf(stderr, "(cocoa) can't create a view\n"); exit(1); } // create a window - normalWindow = [[NSWindow alloc] initWithContentRect:[cocoaView frame] + frame.origin = NSMakePoint(256.0, 256.0); + normalWindow = [[NSWindow alloc] initWithContentRect:frame styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskMiniaturizable|NSWindowStyleMaskClosable backing:NSBackingStoreBuffered defer:NO]; if(!normalWindow) { @@ -1124,7 +1154,7 @@ - (id) init exit(1); } [normalWindow setAcceptsMouseMovedEvents:YES]; - [normalWindow setTitle:@"QEMU"]; + [normalWindow setTitle:normalWindowName()]; [normalWindow setContentView:cocoaView]; #if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10) [normalWindow useOptimizedDrawing:YES]; @@ -1132,6 +1162,7 @@ - (id) init [normalWindow makeKeyAndOrderFront:self]; [normalWindow center]; [normalWindow setDelegate: self]; + stretch_video = false; /* Used for displaying pause on the screen */ @@ -1205,6 +1236,11 @@ - (BOOL)windowShouldClose:(id)sender return NO; } +- (void) windowDidMove:(id)sender +{ + [cocoaView saveWindowPosition]; +} + /* Called when QEMU goes into the background */ - (void) applicationWillResignActive: (NSNotification *)aNotification { @@ -1537,7 +1573,7 @@ - (void)adjustSpeed:(id)sender }); COCOA_DEBUG("cpu throttling at %d%c\n", cpu_throttle_get_percentage(), '%'); } - + @end @interface QemuApplication : NSApplication