<commit_before>//===-- sanitizer_win.cc --------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is shared between AddressSanitizer and ThreadSanitizer
// run-time libraries and implements windows-specific functions from
// sanitizer_libc.h.
//===----------------------------------------------------------------------===//

#include "sanitizer_platform.h"
#if SANITIZER_WINDOWS

#define WIN32_LEAN_AND_MEAN
#define NOGDI
#include <stdlib.h>
#include <io.h>
#include <windows.h>

#include "sanitizer_common.h"
#include "sanitizer_libc.h"
#include "sanitizer_mutex.h"
#include "sanitizer_placement_new.h"
#include "sanitizer_stacktrace.h"

namespace __sanitizer {

#include "sanitizer_syscall_generic.inc"

// --------------------- sanitizer_common.h
uptr GetPageSize() {
  return 1U << 14;  // FIXME: is this configurable?
}

uptr GetMmapGranularity() {
  return 1U << 16;  // FIXME: is this configurable?
}

bool FileExists(const char *filename) {
  UNIMPLEMENTED();
}

uptr internal_getpid() {
  return GetProcessId(GetCurrentProcess());
}

// In contrast to POSIX, on Windows GetCurrentThreadId()
// returns a system-unique identifier.
uptr GetTid() {
  return GetCurrentThreadId();
}

uptr GetThreadSelf() {
  return GetTid();
}

void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
                                uptr *stack_bottom) {
  CHECK(stack_top);
  CHECK(stack_bottom);
  MEMORY_BASIC_INFORMATION mbi;
  CHECK_NE(VirtualQuery(&mbi /* on stack */, &mbi, sizeof(mbi)), 0);
  // FIXME: is it possible for the stack to not be a single allocation?
  // Are these values what ASan expects to get (reserved, not committed;
  // including stack guard page) ?
  *stack_top = (uptr)mbi.BaseAddress + mbi.RegionSize;
  *stack_bottom = (uptr)mbi.AllocationBase;
}

void *MmapOrDie(uptr size, const char *mem_type) {
  void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
  if (rv == 0) {
    Report("ERROR: Failed to allocate 0x%zx (%zd) bytes of %s\n",
           size, size, mem_type);
    CHECK("unable to mmap" && 0);
  }
  return rv;
}

void UnmapOrDie(void *addr, uptr size) {
  if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) {
    Report("ERROR: Failed to deallocate 0x%zx (%zd) bytes at address %p\n",
           size, size, addr);
    CHECK("unable to unmap" && 0);
  }
}

void *MmapFixedNoReserve(uptr fixed_addr, uptr size) {
  // FIXME: is this really "NoReserve"? On Win32 this does not matter much,
  // but on Win64 it does.
  void *p = VirtualAlloc((LPVOID)fixed_addr, size,
      MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
  if (p == 0)
    Report("ERROR: Failed to allocate 0x%zx (%zd) bytes at %p (%d)\n",
           size, size, fixed_addr, GetLastError());
  return p;
}

void *MmapFixedOrDie(uptr fixed_addr, uptr size) {
  return MmapFixedNoReserve(fixed_addr, size);
}

void *Mprotect(uptr fixed_addr, uptr size) {
  return VirtualAlloc((LPVOID)fixed_addr, size,
                      MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS);
}

void FlushUnneededShadowMemory(uptr addr, uptr size) {
  // This is almost useless on 32-bits.
  // FIXME: add madvice-analog when we move to 64-bits.
}

bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
  // FIXME: shall we do anything here on Windows?
  return true;
}

void *MapFileToMemory(const char *file_name, uptr *buff_size) {
  UNIMPLEMENTED();
}

static const int kMaxEnvNameLength = 128;
static const DWORD kMaxEnvValueLength = 32767;

namespace {

struct EnvVariable {
  char name[kMaxEnvNameLength];
  char value[kMaxEnvValueLength];
};

}  // namespace

static const int kEnvVariables = 5;
static EnvVariable env_vars[kEnvVariables];
static int num_env_vars;

const char *GetEnv(const char *name) {
  // Note: this implementation caches the values of the environment variables
  // and limits their quantity.
  for (int i = 0; i < num_env_vars; i++) {
    if (0 == internal_strcmp(name, env_vars[i].name))
      return env_vars[i].value;
  }
  CHECK_LT(num_env_vars, kEnvVariables);
  DWORD rv = GetEnvironmentVariableA(name, env_vars[num_env_vars].value,
                                     kMaxEnvValueLength);
  if (rv > 0 && rv < kMaxEnvValueLength) {
    CHECK_LT(internal_strlen(name), kMaxEnvNameLength);
    internal_strncpy(env_vars[num_env_vars].name, name, kMaxEnvNameLength);
    num_env_vars++;
    return env_vars[num_env_vars - 1].value;
  }
  return 0;
}

const char *GetPwd() {
  UNIMPLEMENTED();
}

u32 GetUid() {
  UNIMPLEMENTED();
}

void DumpProcessMap() {
  UNIMPLEMENTED();
}

void DisableCoreDumper() {
  UNIMPLEMENTED();
}

void ReExec() {
  UNIMPLEMENTED();
}

void PrepareForSandboxing() {
  // Nothing here for now.
}

bool StackSizeIsUnlimited() {
  UNIMPLEMENTED();
}

void SetStackSizeLimitInBytes(uptr limit) {
  UNIMPLEMENTED();
}

void SleepForSeconds(int seconds) {
  Sleep(seconds * 1000);
}

void SleepForMillis(int millis) {
  Sleep(millis);
}

u64 NanoTime() {
  return 0;
}

void Abort() {
  abort();
  _exit(-1);  // abort is not NORETURN on Windows.
}

#ifndef SANITIZER_GO
int Atexit(void (*function)(void)) {
  return atexit(function);
}
#endif

// ------------------ sanitizer_libc.h
uptr internal_mmap(void *addr, uptr length, int prot, int flags,
                   int fd, u64 offset) {
  UNIMPLEMENTED();
}

uptr internal_munmap(void *addr, uptr length) {
  UNIMPLEMENTED();
}

uptr internal_close(fd_t fd) {
  UNIMPLEMENTED();
}

int internal_isatty(fd_t fd) {
  return _isatty(fd);
}

uptr internal_open(const char *filename, int flags) {
  UNIMPLEMENTED();
}

uptr internal_open(const char *filename, int flags, u32 mode) {
  UNIMPLEMENTED();
}

uptr OpenFile(const char *filename, bool write) {
  UNIMPLEMENTED();
}

uptr internal_read(fd_t fd, void *buf, uptr count) {
  UNIMPLEMENTED();
}

uptr internal_write(fd_t fd, const void *buf, uptr count) {
  if (fd != kStderrFd)
    UNIMPLEMENTED();
  HANDLE err = GetStdHandle(STD_ERROR_HANDLE);
  if (err == 0)
    return 0;  // FIXME: this might not work on some apps.
  DWORD ret;
  if (!WriteFile(err, buf, count, &ret, 0))
    return 0;
  return ret;
}

uptr internal_stat(const char *path, void *buf) {
  UNIMPLEMENTED();
}

uptr internal_lstat(const char *path, void *buf) {
  UNIMPLEMENTED();
}

uptr internal_fstat(fd_t fd, void *buf) {
  UNIMPLEMENTED();
}

uptr internal_filesize(fd_t fd) {
  UNIMPLEMENTED();
}

uptr internal_dup2(int oldfd, int newfd) {
  UNIMPLEMENTED();
}

uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
  UNIMPLEMENTED();
}

uptr internal_sched_yield() {
  Sleep(0);
  return 0;
}

void internal__exit(int exitcode) {
  _exit(exitcode);
}

// ---------------------- BlockingMutex ---------------- {{{1
const uptr LOCK_UNINITIALIZED = 0;
const uptr LOCK_READY = (uptr)-1;

BlockingMutex::BlockingMutex(LinkerInitialized li) {
  // FIXME: see comments in BlockingMutex::Lock() for the details.
  CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED);

  CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
  InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
  owner_ = LOCK_READY;
}

BlockingMutex::BlockingMutex() {
  CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
  InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
  owner_ = LOCK_READY;
}

void BlockingMutex::Lock() {
  if (owner_ == LOCK_UNINITIALIZED) {
    // FIXME: hm, global BlockingMutex objects are not initialized?!?
    // This might be a side effect of the clang+cl+link Frankenbuild...
    new(this) BlockingMutex((LinkerInitialized)(LINKER_INITIALIZED + 1));

    // FIXME: If it turns out the linker doesn't invoke our
    // constructors, we should probably manually Lock/Unlock all the global
    // locks while we're starting in one thread to avoid double-init races.
  }
  EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
  CHECK_EQ(owner_, LOCK_READY);
  owner_ = GetThreadSelf();
}

void BlockingMutex::Unlock() {
  CHECK_EQ(owner_, GetThreadSelf());
  owner_ = LOCK_READY;
  LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
}

void BlockingMutex::CheckLocked() {
  CHECK_EQ(owner_, GetThreadSelf());
}

uptr GetTlsSize() {
  return 0;
}

void InitTlsSize() {
}

void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
                          uptr *tls_addr, uptr *tls_size) {
  uptr stack_top, stack_bottom;
  GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
  *stk_addr = stack_bottom;
  *stk_size = stack_top - stack_bottom;
  *tls_addr = 0;
  *tls_size = 0;
}

void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp,
                   uptr stack_top, uptr stack_bottom, bool fast) {
  (void)fast;
  (void)stack_top;
  (void)stack_bottom;
  stack->max_size = max_s;
  void *tmp[kStackTraceMax];

  // FIXME: CaptureStackBackTrace might be too slow for us.
  // FIXME: Compare with StackWalk64.
  // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc
  uptr cs_ret = CaptureStackBackTrace(1, stack->max_size, tmp, 0);
  uptr offset = 0;
  // Skip the RTL frames by searching for the PC in the stacktrace.
  // FIXME: this doesn't work well for the malloc/free stacks yet.
  for (uptr i = 0; i < cs_ret; i++) {
    if (pc != (uptr)tmp[i])
      continue;
    offset = i;
    break;
  }

  stack->size = cs_ret - offset;
  for (uptr i = 0; i < stack->size; i++)
    stack->trace[i] = (uptr)tmp[i + offset];
}

}  // namespace __sanitizer

#endif  // _WIN32
<commit_msg>tsan: fix windows crash (incorrect stack boundaries)<commit_after>//===-- sanitizer_win.cc --------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is shared between AddressSanitizer and ThreadSanitizer
// run-time libraries and implements windows-specific functions from
// sanitizer_libc.h.
//===----------------------------------------------------------------------===//

#include "sanitizer_platform.h"
#if SANITIZER_WINDOWS

#define WIN32_LEAN_AND_MEAN
#define NOGDI
#include <stdlib.h>
#include <io.h>
#include <windows.h>

#include "sanitizer_common.h"
#include "sanitizer_libc.h"
#include "sanitizer_mutex.h"
#include "sanitizer_placement_new.h"
#include "sanitizer_stacktrace.h"

namespace __sanitizer {

#include "sanitizer_syscall_generic.inc"

// --------------------- sanitizer_common.h
uptr GetPageSize() {
  return 1U << 14;  // FIXME: is this configurable?
}

uptr GetMmapGranularity() {
  return 1U << 16;  // FIXME: is this configurable?
}

bool FileExists(const char *filename) {
  UNIMPLEMENTED();
}

uptr internal_getpid() {
  return GetProcessId(GetCurrentProcess());
}

// In contrast to POSIX, on Windows GetCurrentThreadId()
// returns a system-unique identifier.
uptr GetTid() {
  return GetCurrentThreadId();
}

uptr GetThreadSelf() {
  return GetTid();
}

void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
                                uptr *stack_bottom) {
  CHECK(stack_top);
  CHECK(stack_bottom);
  MEMORY_BASIC_INFORMATION mbi;
  CHECK_NE(VirtualQuery(&mbi /* on stack */, &mbi, sizeof(mbi)), 0);
  // FIXME: is it possible for the stack to not be a single allocation?
  // Are these values what ASan expects to get (reserved, not committed;
  // including stack guard page) ?
  *stack_top = (uptr)mbi.BaseAddress + mbi.RegionSize;
  *stack_bottom = (uptr)mbi.AllocationBase;
}

void *MmapOrDie(uptr size, const char *mem_type) {
  void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
  if (rv == 0) {
    Report("ERROR: Failed to allocate 0x%zx (%zd) bytes of %s\n",
           size, size, mem_type);
    CHECK("unable to mmap" && 0);
  }
  return rv;
}

void UnmapOrDie(void *addr, uptr size) {
  if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) {
    Report("ERROR: Failed to deallocate 0x%zx (%zd) bytes at address %p\n",
           size, size, addr);
    CHECK("unable to unmap" && 0);
  }
}

void *MmapFixedNoReserve(uptr fixed_addr, uptr size) {
  // FIXME: is this really "NoReserve"? On Win32 this does not matter much,
  // but on Win64 it does.
  void *p = VirtualAlloc((LPVOID)fixed_addr, size,
      MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
  if (p == 0)
    Report("ERROR: Failed to allocate 0x%zx (%zd) bytes at %p (%d)\n",
           size, size, fixed_addr, GetLastError());
  return p;
}

void *MmapFixedOrDie(uptr fixed_addr, uptr size) {
  return MmapFixedNoReserve(fixed_addr, size);
}

void *Mprotect(uptr fixed_addr, uptr size) {
  return VirtualAlloc((LPVOID)fixed_addr, size,
                      MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS);
}

void FlushUnneededShadowMemory(uptr addr, uptr size) {
  // This is almost useless on 32-bits.
  // FIXME: add madvice-analog when we move to 64-bits.
}

bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
  // FIXME: shall we do anything here on Windows?
  return true;
}

void *MapFileToMemory(const char *file_name, uptr *buff_size) {
  UNIMPLEMENTED();
}

static const int kMaxEnvNameLength = 128;
static const DWORD kMaxEnvValueLength = 32767;

namespace {

struct EnvVariable {
  char name[kMaxEnvNameLength];
  char value[kMaxEnvValueLength];
};

}  // namespace

static const int kEnvVariables = 5;
static EnvVariable env_vars[kEnvVariables];
static int num_env_vars;

const char *GetEnv(const char *name) {
  // Note: this implementation caches the values of the environment variables
  // and limits their quantity.
  for (int i = 0; i < num_env_vars; i++) {
    if (0 == internal_strcmp(name, env_vars[i].name))
      return env_vars[i].value;
  }
  CHECK_LT(num_env_vars, kEnvVariables);
  DWORD rv = GetEnvironmentVariableA(name, env_vars[num_env_vars].value,
                                     kMaxEnvValueLength);
  if (rv > 0 && rv < kMaxEnvValueLength) {
    CHECK_LT(internal_strlen(name), kMaxEnvNameLength);
    internal_strncpy(env_vars[num_env_vars].name, name, kMaxEnvNameLength);
    num_env_vars++;
    return env_vars[num_env_vars - 1].value;
  }
  return 0;
}

const char *GetPwd() {
  UNIMPLEMENTED();
}

u32 GetUid() {
  UNIMPLEMENTED();
}

void DumpProcessMap() {
  UNIMPLEMENTED();
}

void DisableCoreDumper() {
  UNIMPLEMENTED();
}

void ReExec() {
  UNIMPLEMENTED();
}

void PrepareForSandboxing() {
  // Nothing here for now.
}

bool StackSizeIsUnlimited() {
  UNIMPLEMENTED();
}

void SetStackSizeLimitInBytes(uptr limit) {
  UNIMPLEMENTED();
}

void SleepForSeconds(int seconds) {
  Sleep(seconds * 1000);
}

void SleepForMillis(int millis) {
  Sleep(millis);
}

u64 NanoTime() {
  return 0;
}

u64 NanoTime() {
  return 0;
}

void Abort() {
  abort();
  _exit(-1);  // abort is not NORETURN on Windows.
}

#ifndef SANITIZER_GO
int Atexit(void (*function)(void)) {
  return atexit(function);
}
#endif

// ------------------ sanitizer_libc.h
uptr internal_mmap(void *addr, uptr length, int prot, int flags,
                   int fd, u64 offset) {
  UNIMPLEMENTED();
}

uptr internal_munmap(void *addr, uptr length) {
  UNIMPLEMENTED();
}

uptr internal_close(fd_t fd) {
  UNIMPLEMENTED();
}

int internal_isatty(fd_t fd) {
  return _isatty(fd);
}

uptr internal_open(const char *filename, int flags) {
  UNIMPLEMENTED();
}

uptr internal_open(const char *filename, int flags, u32 mode) {
  UNIMPLEMENTED();
}

uptr OpenFile(const char *filename, bool write) {
  UNIMPLEMENTED();
}

uptr internal_read(fd_t fd, void *buf, uptr count) {
  UNIMPLEMENTED();
}

uptr internal_write(fd_t fd, const void *buf, uptr count) {
  if (fd != kStderrFd)
    UNIMPLEMENTED();
  HANDLE err = GetStdHandle(STD_ERROR_HANDLE);
  if (err == 0)
    return 0;  // FIXME: this might not work on some apps.
  DWORD ret;
  if (!WriteFile(err, buf, count, &ret, 0))
    return 0;
  return ret;
}

uptr internal_stat(const char *path, void *buf) {
  UNIMPLEMENTED();
}

uptr internal_lstat(const char *path, void *buf) {
  UNIMPLEMENTED();
}

uptr internal_fstat(fd_t fd, void *buf) {
  UNIMPLEMENTED();
}

uptr internal_filesize(fd_t fd) {
  UNIMPLEMENTED();
}

uptr internal_dup2(int oldfd, int newfd) {
  UNIMPLEMENTED();
}

uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
  UNIMPLEMENTED();
}

uptr internal_sched_yield() {
  Sleep(0);
  return 0;
}

void internal__exit(int exitcode) {
  _exit(exitcode);
}

// ---------------------- BlockingMutex ---------------- {{{1
const uptr LOCK_UNINITIALIZED = 0;
const uptr LOCK_READY = (uptr)-1;

BlockingMutex::BlockingMutex(LinkerInitialized li) {
  // FIXME: see comments in BlockingMutex::Lock() for the details.
  CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED);

  CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
  InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
  owner_ = LOCK_READY;
}

BlockingMutex::BlockingMutex() {
  CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
  InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
  owner_ = LOCK_READY;
}

void BlockingMutex::Lock() {
  if (owner_ == LOCK_UNINITIALIZED) {
    // FIXME: hm, global BlockingMutex objects are not initialized?!?
    // This might be a side effect of the clang+cl+link Frankenbuild...
    new(this) BlockingMutex((LinkerInitialized)(LINKER_INITIALIZED + 1));

    // FIXME: If it turns out the linker doesn't invoke our
    // constructors, we should probably manually Lock/Unlock all the global
    // locks while we're starting in one thread to avoid double-init races.
  }
  EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
  CHECK_EQ(owner_, LOCK_READY);
  owner_ = GetThreadSelf();
}

void BlockingMutex::Unlock() {
  CHECK_EQ(owner_, GetThreadSelf());
  owner_ = LOCK_READY;
  LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
}

void BlockingMutex::CheckLocked() {
  CHECK_EQ(owner_, GetThreadSelf());
}

uptr GetTlsSize() {
  return 0;
}

void InitTlsSize() {
}

void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
                          uptr *tls_addr, uptr *tls_size) {
#ifdef SANITIZER_GO
  *stk_addr = 0;
  *stk_size = 0;
  *tls_addr = 0;
  *tls_size = 0;
#else
  uptr stack_top, stack_bottom;
  GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
  *stk_addr = stack_bottom;
  *stk_size = stack_top - stack_bottom;
  *tls_addr = 0;
  *tls_size = 0;
#endif
}

void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp,
                   uptr stack_top, uptr stack_bottom, bool fast) {
  (void)fast;
  (void)stack_top;
  (void)stack_bottom;
  stack->max_size = max_s;
  void *tmp[kStackTraceMax];

  // FIXME: CaptureStackBackTrace might be too slow for us.
  // FIXME: Compare with StackWalk64.
  // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc
  uptr cs_ret = CaptureStackBackTrace(1, stack->max_size, tmp, 0);
  uptr offset = 0;
  // Skip the RTL frames by searching for the PC in the stacktrace.
  // FIXME: this doesn't work well for the malloc/free stacks yet.
  for (uptr i = 0; i < cs_ret; i++) {
    if (pc != (uptr)tmp[i])
      continue;
    offset = i;
    break;
  }

  stack->size = cs_ret - offset;
  for (uptr i = 0; i < stack->size; i++)
    stack->trace[i] = (uptr)tmp[i + offset];
}

}  // namespace __sanitizer

#endif  // _WIN32
<|endoftext|>


<commit_before>#include "OSGWidget.h"
#include "PickHandler.h"

#include <osg/Camera>

#include <osg/DisplaySettings>
#include <osg/Geode>
#include <osg/Material>
#include <osg/Shape>
#include <osg/ShapeDrawable>
#include <osg/StateSet>

#include <osgDB/WriteFile>

#include <osgGA/EventQueue>
#include <osgGA/TrackballManipulator>

#include <osgUtil/IntersectionVisitor>
#include <osgUtil/PolytopeIntersector>

#include <osgViewer/View>
#include <osgViewer/ViewerEventHandlers>

#include <cassert>

#include <stdexcept>
#include <vector>

#include <QDebug>
#include <QKeyEvent>
#include <QPainter>
#include <QWheelEvent>

namespace
{

#ifdef WITH_SELECTION_PROCESSING
QRect makeRectangle( const QPoint& first, const QPoint& second )
{
  // Relative to the first point, the second point may be in either one of the
  // four quadrants of an Euclidean coordinate system.
  //
  // We enumerate them in counter-clockwise order, starting from the lower-right
  // quadrant that corresponds to the default case:
  //
  //            |
  //       (3)  |  (4)
  //            |
  //     -------|-------
  //            |
  //       (2)  |  (1)
  //            |

  if( second.x() >= first.x() && second.y() >= first.y() )
    return QRect( first, second );
  else if( second.x() < first.x() && second.y() >= first.y() )
    return QRect( QPoint( second.x(), first.y() ), QPoint( first.x(), second.y() ) );
  else if( second.x() < first.x() && second.y() < first.y() )
    return QRect( second, first );
  else if( second.x() >= first.x() && second.y() < first.y() )
    return QRect( QPoint( first.x(), second.y() ), QPoint( second.x(), first.y() ) );

  // Should never reach that point...
  return QRect();
}
#endif

}

OSGWidget::OSGWidget( QWidget* parent,
                      Qt::WindowFlags f )
  : QOpenGLWidget( parent,
                   f )
  , graphicsWindow_( new osgViewer::GraphicsWindowEmbedded( this->x(),
                                                            this->y(),
                                                            this->width(),
                                                            this->height() ) )
  , viewer_( new osgViewer::CompositeViewer )
  , selectionActive_( false )
  , selectionFinished_( true )
{
  osg::Sphere* sphere    = new osg::Sphere( osg::Vec3( 0.f, 0.f, 0.f ), 0.25f );
  osg::ShapeDrawable* sd = new osg::ShapeDrawable( sphere );
  sd->setColor( osg::Vec4( 1.f, 0.f, 0.f, 1.f ) );
  sd->setName( "A nice sphere" );

  osg::Geode* geode = new osg::Geode;
  geode->addDrawable( sd );

  // Set material for basic lighting and enable depth tests. Else, the sphere
  // will suffer from rendering errors.
  {
    osg::StateSet* stateSet = geode->getOrCreateStateSet();
    osg::Material* material = new osg::Material;

    material->setColorMode( osg::Material::AMBIENT_AND_DIFFUSE );

    stateSet->setAttributeAndModes( material, osg::StateAttribute::ON );
    stateSet->setMode( GL_DEPTH_TEST, osg::StateAttribute::ON );
  }

  float aspectRatio = static_cast<float>( this->width() / 2 ) / static_cast<float>( this->height() );

  osg::Camera* camera = new osg::Camera;
  camera->setViewport( 0, 0, this->width() / 2, this->height() );
  camera->setClearColor( osg::Vec4( 0.f, 0.f, 1.f, 1.f ) );
  camera->setProjectionMatrixAsPerspective( 30.f, aspectRatio, 1.f, 1000.f );
  camera->setGraphicsContext( graphicsWindow_ );

  osgViewer::View* view = new osgViewer::View;
  view->setCamera( camera );
  view->setSceneData( geode );
  view->addEventHandler( new osgViewer::StatsHandler );
#ifdef WITH_PICK_HANDLER
  view->addEventHandler( new PickHandler );
#endif

  osgGA::TrackballManipulator* manipulator = new osgGA::TrackballManipulator;
  manipulator->setAllowThrow( false );

  view->setCameraManipulator( manipulator );

  osg::Camera* sideCamera = new osg::Camera;
  sideCamera->setViewport( this->width() /2, 0,
                           this->width() /2, this->height() );

  sideCamera->setClearColor( osg::Vec4( 0.f, 0.f, 1.f, 1.f ) );
  sideCamera->setProjectionMatrixAsPerspective( 30.f, aspectRatio, 1.f, 1000.f );
  sideCamera->setGraphicsContext( graphicsWindow_ );

  osgViewer::View* sideView = new osgViewer::View;
  sideView->setCamera( sideCamera );
  sideView->setSceneData( geode );
  sideView->addEventHandler( new osgViewer::StatsHandler );
  sideView->setCameraManipulator( new osgGA::TrackballManipulator );

  viewer_->addView( view );
  viewer_->addView( sideView );
  viewer_->setThreadingModel( osgViewer::CompositeViewer::SingleThreaded );
  viewer_->realize();

  // This ensures that the widget will receive keyboard events. This focus
  // policy is not set by default. The default, Qt::NoFocus, will result in
  // keyboard events that are ignored.
  this->setFocusPolicy( Qt::StrongFocus );
  this->setMinimumSize( 100, 100 );

  // Ensures that the widget receives mouse move events even though no
  // mouse button has been pressed. We require this in order to let the
  // graphics window switch viewports properly.
  this->setMouseTracking( true );
}

OSGWidget::~OSGWidget()
{
}

void OSGWidget::paintEvent( QPaintEvent* /* paintEvent */ )
{
  this->makeCurrent();

  QPainter painter( this );
  painter.setRenderHint( QPainter::Antialiasing );

  this->paintGL();

#ifdef WITH_SELECTION_PROCESSING
  if( selectionActive_ && !selectionFinished_ )
  {
    painter.setPen( Qt::black );
    painter.setBrush( Qt::transparent );
    painter.drawRect( makeRectangle( selectionStart_, selectionEnd_ ) );
  }
#endif

  painter.end();

  this->doneCurrent();
}

void OSGWidget::paintGL()
{
  viewer_->frame();
}

void OSGWidget::resizeGL( int width, int height )
{
  this->getEventQueue()->windowResize( this->x(), this->y(), width, height );
  graphicsWindow_->resized( this->x(), this->y(), width, height );

  this->onResize( width, height );
}

void OSGWidget::keyPressEvent( QKeyEvent* event )
{
  QString keyString   = event->text();
  const char* keyData = keyString.toLocal8Bit().data();

  if( event->key() == Qt::Key_S )
  {
#ifdef WITH_SELECTION_PROCESSING
    selectionActive_ = !selectionActive_;
#endif

    // Further processing is required for the statistics handler here, so we do
    // not return right away.
  }
  else if( event->key() == Qt::Key_D )
  {
    osgDB::writeNodeFile( *viewer_->getView(0)->getSceneData(),
                          "/tmp/sceneGraph.osg" );

    return;
  }
  else if( event->key() == Qt::Key_H )
  {
    this->onHome();
    return;
  }

  this->getEventQueue()->keyPress( osgGA::GUIEventAdapter::KeySymbol( *keyData ) );
}

void OSGWidget::keyReleaseEvent( QKeyEvent* event )
{
  QString keyString   = event->text();
  const char* keyData = keyString.toLocal8Bit().data();

  this->getEventQueue()->keyRelease( osgGA::GUIEventAdapter::KeySymbol( *keyData ) );
}

void OSGWidget::mouseMoveEvent( QMouseEvent* event )
{
  // Note that we have to check the buttons mask in order to see whether the
  // left button has been pressed. A call to `button()` will only result in
  // `Qt::NoButton` for mouse move events.
  if( selectionActive_ && event->buttons() & Qt::LeftButton )
  {
    selectionEnd_ = event->pos();

    // Ensures that new paint events are created while the user moves the
    // mouse.
    this->update();
  }
  else
  {
    this->getEventQueue()->mouseMotion( static_cast<float>( event->x() ),
                                        static_cast<float>( event->y() ) );
  }
}

void OSGWidget::mousePressEvent( QMouseEvent* event )
{
  // Selection processing
  if( selectionActive_ && event->button() == Qt::LeftButton )
  {
    selectionStart_    = event->pos();
    selectionEnd_      = selectionStart_; // Deletes the old selection
    selectionFinished_ = false;           // As long as this is set, the rectangle will be drawn
  }

  // Normal processing
  else
  {
    // 1 = left mouse button
    // 2 = middle mouse button
    // 3 = right mouse button

    unsigned int button = 0;

    switch( event->button() )
    {
    case Qt::LeftButton:
      button = 1;
      break;

    case Qt::MiddleButton:
      button = 2;
      break;

    case Qt::RightButton:
      button = 3;
      break;

    default:
      break;
    }

    this->getEventQueue()->mouseButtonPress( static_cast<float>( event->x() ),
                                             static_cast<float>( event->y() ),
                                             button );
    }
}

void OSGWidget::mouseReleaseEvent(QMouseEvent* event)
{
  // Selection processing: Store end position and obtain selected objects
  // through polytope intersection.
  if( selectionActive_ && event->button() == Qt::LeftButton )
  {
    selectionEnd_      = event->pos();
    selectionFinished_ = true; // Will force the painter to stop drawing the
                               // selection rectangle

    this->processSelection();
  }

  // Normal processing
  else
  {
    // 1 = left mouse button
    // 2 = middle mouse button
    // 3 = right mouse button

    unsigned int button = 0;

    switch( event->button() )
    {
    case Qt::LeftButton:
      button = 1;
      break;

    case Qt::MiddleButton:
      button = 2;
      break;

    case Qt::RightButton:
      button = 3;
      break;

    default:
      break;
    }

    this->getEventQueue()->mouseButtonRelease( static_cast<float>( event->x() ),
                                               static_cast<float>( event->y() ),
                                               button );
  }
}

void OSGWidget::wheelEvent( QWheelEvent* event )
{
  // Ignore wheel events as long as the selection is active.
  if( selectionActive_ )
    return;

  event->accept();
  int delta = event->delta();

  osgGA::GUIEventAdapter::ScrollingMotion motion = delta > 0 ?   osgGA::GUIEventAdapter::SCROLL_UP
                                                               : osgGA::GUIEventAdapter::SCROLL_DOWN;

  this->getEventQueue()->mouseScroll( motion );
}

bool OSGWidget::event( QEvent* event )
{
  bool handled = QOpenGLWidget::event( event );

  // This ensures that the OSG widget is always going to be repainted after the
  // user performed some interaction. Doing this in the event handler ensures
  // that we don't forget about some event and prevents duplicate code.
  switch( event->type() )
  {
  case QEvent::KeyPress:
  case QEvent::KeyRelease:
  case QEvent::MouseButtonDblClick:
  case QEvent::MouseButtonPress:
  case QEvent::MouseButtonRelease:
  case QEvent::MouseMove:
  case QEvent::Wheel:
    this->update();
    break;

  default:
    break;
  }

  return handled;
}

void OSGWidget::onHome()
{
  osgViewer::ViewerBase::Views views;
  viewer_->getViews( views );

  for( std::size_t i = 0; i < views.size(); i++ )
  {
    osgViewer::View* view = views.at(i);
    view->home();
  }
}

void OSGWidget::onResize( int width, int height )
{
  std::vector<osg::Camera*> cameras;
  viewer_->getCameras( cameras );

  assert( cameras.size() == 2 );

  cameras[0]->setViewport( 0, 0, this->width() / 2, this->height() );
  cameras[1]->setViewport( this->width() / 2, 0, this->width() / 2, this->height() );
}

osgGA::EventQueue* OSGWidget::getEventQueue() const
{
  osgGA::EventQueue* eventQueue = graphicsWindow_->getEventQueue();

  if( eventQueue )
    return eventQueue;
  else
    throw std::runtime_error( "Unable to obtain valid event queue");
}

void OSGWidget::processSelection()
{
#ifdef WITH_SELECTION_PROCESSING
  QRect selectionRectangle = makeRectangle( selectionStart_, selectionEnd_ );
  int widgetHeight         = this->height();

  double xMin = selectionRectangle.left();
  double xMax = selectionRectangle.right();
  double yMin = widgetHeight - selectionRectangle.bottom();
  double yMax = widgetHeight - selectionRectangle.top();

  osgUtil::PolytopeIntersector* polytopeIntersector
      = new osgUtil::PolytopeIntersector( osgUtil::PolytopeIntersector::WINDOW,
                                          xMin, yMin,
                                          xMax, yMax );

  // This limits the amount of intersections that are reported by the
  // polytope intersector. Using this setting, a single drawable will
  // appear at most once while calculating intersections. This is the
  // preferred and expected behaviour.
  polytopeIntersector->setIntersectionLimit( osgUtil::Intersector::LIMIT_ONE_PER_DRAWABLE );

  osgUtil::IntersectionVisitor iv( polytopeIntersector );

  for( unsigned int viewIndex = 0; viewIndex < viewer_->getNumViews(); viewIndex++ )
  {
    osgViewer::View* view = viewer_->getView( viewIndex );

    if( !view )
      throw std::runtime_error( "Unable to obtain valid view for selection processing" );

    osg::Camera* camera = view->getCamera();

    if( !camera )
      throw std::runtime_error( "Unable to obtain valid camera for selection processing" );

    camera->accept( iv );

    if( !polytopeIntersector->containsIntersections() )
      continue;

    auto intersections = polytopeIntersector->getIntersections();

    for( auto&& intersection : intersections )
      qDebug() << "Selected a drawable:" << QString::fromStdString( intersection.drawable->getName() );
  }
#endif
}
<commit_msg>Fixed warning about unused parameters<commit_after>#include "OSGWidget.h"
#include "PickHandler.h"

#include <osg/Camera>

#include <osg/DisplaySettings>
#include <osg/Geode>
#include <osg/Material>
#include <osg/Shape>
#include <osg/ShapeDrawable>
#include <osg/StateSet>

#include <osgDB/WriteFile>

#include <osgGA/EventQueue>
#include <osgGA/TrackballManipulator>

#include <osgUtil/IntersectionVisitor>
#include <osgUtil/PolytopeIntersector>

#include <osgViewer/View>
#include <osgViewer/ViewerEventHandlers>

#include <cassert>

#include <stdexcept>
#include <vector>

#include <QDebug>
#include <QKeyEvent>
#include <QPainter>
#include <QWheelEvent>

namespace
{

#ifdef WITH_SELECTION_PROCESSING
QRect makeRectangle( const QPoint& first, const QPoint& second )
{
  // Relative to the first point, the second point may be in either one of the
  // four quadrants of an Euclidean coordinate system.
  //
  // We enumerate them in counter-clockwise order, starting from the lower-right
  // quadrant that corresponds to the default case:
  //
  //            |
  //       (3)  |  (4)
  //            |
  //     -------|-------
  //            |
  //       (2)  |  (1)
  //            |

  if( second.x() >= first.x() && second.y() >= first.y() )
    return QRect( first, second );
  else if( second.x() < first.x() && second.y() >= first.y() )
    return QRect( QPoint( second.x(), first.y() ), QPoint( first.x(), second.y() ) );
  else if( second.x() < first.x() && second.y() < first.y() )
    return QRect( second, first );
  else if( second.x() >= first.x() && second.y() < first.y() )
    return QRect( QPoint( first.x(), second.y() ), QPoint( second.x(), first.y() ) );

  // Should never reach that point...
  return QRect();
}
#endif

}

OSGWidget::OSGWidget( QWidget* parent,
                      Qt::WindowFlags f )
  : QOpenGLWidget( parent,
                   f )
  , graphicsWindow_( new osgViewer::GraphicsWindowEmbedded( this->x(),
                                                            this->y(),
                                                            this->width(),
                                                            this->height() ) )
  , viewer_( new osgViewer::CompositeViewer )
  , selectionActive_( false )
  , selectionFinished_( true )
{
  osg::Sphere* sphere    = new osg::Sphere( osg::Vec3( 0.f, 0.f, 0.f ), 0.25f );
  osg::ShapeDrawable* sd = new osg::ShapeDrawable( sphere );
  sd->setColor( osg::Vec4( 1.f, 0.f, 0.f, 1.f ) );
  sd->setName( "A nice sphere" );

  osg::Geode* geode = new osg::Geode;
  geode->addDrawable( sd );

  // Set material for basic lighting and enable depth tests. Else, the sphere
  // will suffer from rendering errors.
  {
    osg::StateSet* stateSet = geode->getOrCreateStateSet();
    osg::Material* material = new osg::Material;

    material->setColorMode( osg::Material::AMBIENT_AND_DIFFUSE );

    stateSet->setAttributeAndModes( material, osg::StateAttribute::ON );
    stateSet->setMode( GL_DEPTH_TEST, osg::StateAttribute::ON );
  }

  float aspectRatio = static_cast<float>( this->width() / 2 ) / static_cast<float>( this->height() );

  osg::Camera* camera = new osg::Camera;
  camera->setViewport( 0, 0, this->width() / 2, this->height() );
  camera->setClearColor( osg::Vec4( 0.f, 0.f, 1.f, 1.f ) );
  camera->setProjectionMatrixAsPerspective( 30.f, aspectRatio, 1.f, 1000.f );
  camera->setGraphicsContext( graphicsWindow_ );

  osgViewer::View* view = new osgViewer::View;
  view->setCamera( camera );
  view->setSceneData( geode );
  view->addEventHandler( new osgViewer::StatsHandler );
#ifdef WITH_PICK_HANDLER
  view->addEventHandler( new PickHandler );
#endif

  osgGA::TrackballManipulator* manipulator = new osgGA::TrackballManipulator;
  manipulator->setAllowThrow( false );

  view->setCameraManipulator( manipulator );

  osg::Camera* sideCamera = new osg::Camera;
  sideCamera->setViewport( this->width() /2, 0,
                           this->width() /2, this->height() );

  sideCamera->setClearColor( osg::Vec4( 0.f, 0.f, 1.f, 1.f ) );
  sideCamera->setProjectionMatrixAsPerspective( 30.f, aspectRatio, 1.f, 1000.f );
  sideCamera->setGraphicsContext( graphicsWindow_ );

  osgViewer::View* sideView = new osgViewer::View;
  sideView->setCamera( sideCamera );
  sideView->setSceneData( geode );
  sideView->addEventHandler( new osgViewer::StatsHandler );
  sideView->setCameraManipulator( new osgGA::TrackballManipulator );

  viewer_->addView( view );
  viewer_->addView( sideView );
  viewer_->setThreadingModel( osgViewer::CompositeViewer::SingleThreaded );
  viewer_->realize();

  // This ensures that the widget will receive keyboard events. This focus
  // policy is not set by default. The default, Qt::NoFocus, will result in
  // keyboard events that are ignored.
  this->setFocusPolicy( Qt::StrongFocus );
  this->setMinimumSize( 100, 100 );

  // Ensures that the widget receives mouse move events even though no
  // mouse button has been pressed. We require this in order to let the
  // graphics window switch viewports properly.
  this->setMouseTracking( true );
}

OSGWidget::~OSGWidget()
{
}

void OSGWidget::paintEvent( QPaintEvent* /* paintEvent */ )
{
  this->makeCurrent();

  QPainter painter( this );
  painter.setRenderHint( QPainter::Antialiasing );

  this->paintGL();

#ifdef WITH_SELECTION_PROCESSING
  if( selectionActive_ && !selectionFinished_ )
  {
    painter.setPen( Qt::black );
    painter.setBrush( Qt::transparent );
    painter.drawRect( makeRectangle( selectionStart_, selectionEnd_ ) );
  }
#endif

  painter.end();

  this->doneCurrent();
}

void OSGWidget::paintGL()
{
  viewer_->frame();
}

void OSGWidget::resizeGL( int width, int height )
{
  this->getEventQueue()->windowResize( this->x(), this->y(), width, height );
  graphicsWindow_->resized( this->x(), this->y(), width, height );

  this->onResize( width, height );
}

void OSGWidget::keyPressEvent( QKeyEvent* event )
{
  QString keyString   = event->text();
  const char* keyData = keyString.toLocal8Bit().data();

  if( event->key() == Qt::Key_S )
  {
#ifdef WITH_SELECTION_PROCESSING
    selectionActive_ = !selectionActive_;
#endif

    // Further processing is required for the statistics handler here, so we do
    // not return right away.
  }
  else if( event->key() == Qt::Key_D )
  {
    osgDB::writeNodeFile( *viewer_->getView(0)->getSceneData(),
                          "/tmp/sceneGraph.osg" );

    return;
  }
  else if( event->key() == Qt::Key_H )
  {
    this->onHome();
    return;
  }

  this->getEventQueue()->keyPress( osgGA::GUIEventAdapter::KeySymbol( *keyData ) );
}

void OSGWidget::keyReleaseEvent( QKeyEvent* event )
{
  QString keyString   = event->text();
  const char* keyData = keyString.toLocal8Bit().data();

  this->getEventQueue()->keyRelease( osgGA::GUIEventAdapter::KeySymbol( *keyData ) );
}

void OSGWidget::mouseMoveEvent( QMouseEvent* event )
{
  // Note that we have to check the buttons mask in order to see whether the
  // left button has been pressed. A call to `button()` will only result in
  // `Qt::NoButton` for mouse move events.
  if( selectionActive_ && event->buttons() & Qt::LeftButton )
  {
    selectionEnd_ = event->pos();

    // Ensures that new paint events are created while the user moves the
    // mouse.
    this->update();
  }
  else
  {
    this->getEventQueue()->mouseMotion( static_cast<float>( event->x() ),
                                        static_cast<float>( event->y() ) );
  }
}

void OSGWidget::mousePressEvent( QMouseEvent* event )
{
  // Selection processing
  if( selectionActive_ && event->button() == Qt::LeftButton )
  {
    selectionStart_    = event->pos();
    selectionEnd_      = selectionStart_; // Deletes the old selection
    selectionFinished_ = false;           // As long as this is set, the rectangle will be drawn
  }

  // Normal processing
  else
  {
    // 1 = left mouse button
    // 2 = middle mouse button
    // 3 = right mouse button

    unsigned int button = 0;

    switch( event->button() )
    {
    case Qt::LeftButton:
      button = 1;
      break;

    case Qt::MiddleButton:
      button = 2;
      break;

    case Qt::RightButton:
      button = 3;
      break;

    default:
      break;
    }

    this->getEventQueue()->mouseButtonPress( static_cast<float>( event->x() ),
                                             static_cast<float>( event->y() ),
                                             button );
    }
}

void OSGWidget::mouseReleaseEvent(QMouseEvent* event)
{
  // Selection processing: Store end position and obtain selected objects
  // through polytope intersection.
  if( selectionActive_ && event->button() == Qt::LeftButton )
  {
    selectionEnd_      = event->pos();
    selectionFinished_ = true; // Will force the painter to stop drawing the
                               // selection rectangle

    this->processSelection();
  }

  // Normal processing
  else
  {
    // 1 = left mouse button
    // 2 = middle mouse button
    // 3 = right mouse button

    unsigned int button = 0;

    switch( event->button() )
    {
    case Qt::LeftButton:
      button = 1;
      break;

    case Qt::MiddleButton:
      button = 2;
      break;

    case Qt::RightButton:
      button = 3;
      break;

    default:
      break;
    }

    this->getEventQueue()->mouseButtonRelease( static_cast<float>( event->x() ),
                                               static_cast<float>( event->y() ),
                                               button );
  }
}

void OSGWidget::wheelEvent( QWheelEvent* event )
{
  // Ignore wheel events as long as the selection is active.
  if( selectionActive_ )
    return;

  event->accept();
  int delta = event->delta();

  osgGA::GUIEventAdapter::ScrollingMotion motion = delta > 0 ?   osgGA::GUIEventAdapter::SCROLL_UP
                                                               : osgGA::GUIEventAdapter::SCROLL_DOWN;

  this->getEventQueue()->mouseScroll( motion );
}

bool OSGWidget::event( QEvent* event )
{
  bool handled = QOpenGLWidget::event( event );

  // This ensures that the OSG widget is always going to be repainted after the
  // user performed some interaction. Doing this in the event handler ensures
  // that we don't forget about some event and prevents duplicate code.
  switch( event->type() )
  {
  case QEvent::KeyPress:
  case QEvent::KeyRelease:
  case QEvent::MouseButtonDblClick:
  case QEvent::MouseButtonPress:
  case QEvent::MouseButtonRelease:
  case QEvent::MouseMove:
  case QEvent::Wheel:
    this->update();
    break;

  default:
    break;
  }

  return handled;
}

void OSGWidget::onHome()
{
  osgViewer::ViewerBase::Views views;
  viewer_->getViews( views );

  for( std::size_t i = 0; i < views.size(); i++ )
  {
    osgViewer::View* view = views.at(i);
    view->home();
  }
}

void OSGWidget::onResize( int width, int height )
{
  std::vector<osg::Camera*> cameras;
  viewer_->getCameras( cameras );

  assert( cameras.size() == 2 );

  cameras[0]->setViewport( 0, 0, width / 2, height );
  cameras[1]->setViewport( width / 2, 0, width / 2, height );
}

osgGA::EventQueue* OSGWidget::getEventQueue() const
{
  osgGA::EventQueue* eventQueue = graphicsWindow_->getEventQueue();

  if( eventQueue )
    return eventQueue;
  else
    throw std::runtime_error( "Unable to obtain valid event queue");
}

void OSGWidget::processSelection()
{
#ifdef WITH_SELECTION_PROCESSING
  QRect selectionRectangle = makeRectangle( selectionStart_, selectionEnd_ );
  int widgetHeight         = this->height();

  double xMin = selectionRectangle.left();
  double xMax = selectionRectangle.right();
  double yMin = widgetHeight - selectionRectangle.bottom();
  double yMax = widgetHeight - selectionRectangle.top();

  osgUtil::PolytopeIntersector* polytopeIntersector
      = new osgUtil::PolytopeIntersector( osgUtil::PolytopeIntersector::WINDOW,
                                          xMin, yMin,
                                          xMax, yMax );

  // This limits the amount of intersections that are reported by the
  // polytope intersector. Using this setting, a single drawable will
  // appear at most once while calculating intersections. This is the
  // preferred and expected behaviour.
  polytopeIntersector->setIntersectionLimit( osgUtil::Intersector::LIMIT_ONE_PER_DRAWABLE );

  osgUtil::IntersectionVisitor iv( polytopeIntersector );

  for( unsigned int viewIndex = 0; viewIndex < viewer_->getNumViews(); viewIndex++ )
  {
    osgViewer::View* view = viewer_->getView( viewIndex );

    if( !view )
      throw std::runtime_error( "Unable to obtain valid view for selection processing" );

    osg::Camera* camera = view->getCamera();

    if( !camera )
      throw std::runtime_error( "Unable to obtain valid camera for selection processing" );

    camera->accept( iv );

    if( !polytopeIntersector->containsIntersections() )
      continue;

    auto intersections = polytopeIntersector->getIntersections();

    for( auto&& intersection : intersections )
      qDebug() << "Selected a drawable:" << QString::fromStdString( intersection.drawable->getName() );
  }
#endif
}
<|endoftext|>


<commit_before>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#include <mitkTestingMacros.h>
#include <mitkSurface.h>
#include <mitkIOUtil.h>
#include <vtkCleanPolyData.h>
#include <mitkTestFixture.h>

#include "mitkAnisotropicIterativeClosestPointRegistration.h"
#include "mitkCovarianceMatrixCalculator.h"
#include "mitkAnisotropicRegistrationCommon.h"

/**
 * Test to verify the results of the A-ICP registration.
 * The test runs the standard A-ICP and the trimmed variant.
 */
class mitkAnisotropicIterativeClosestPointRegistrationTestSuite : public mitk::TestFixture
{
  CPPUNIT_TEST_SUITE(mitkAnisotropicIterativeClosestPointRegistrationTestSuite);
  MITK_TEST(testAicpRegistration);
  MITK_TEST(testTrimmedAicpregistration);
  CPPUNIT_TEST_SUITE_END();

private:

  typedef itk::Matrix < double, 3, 3 > Matrix3x3;
  typedef itk::Vector < double, 3 > Vector3;
  typedef std::vector < Matrix3x3 > CovarianceMatrixList;

  mitk::Surface::Pointer m_MovingSurface;
  mitk::Surface::Pointer m_FixedSurface;

  mitk::PointSet::Pointer m_TargetsMovingSurface;
  mitk::PointSet::Pointer m_TargetsFixedSurface;

  CovarianceMatrixList m_SigmasMovingSurface;
  CovarianceMatrixList m_SigmasFixedSurface;

  double m_FRENormalizationFactor;

public:

  /**
   * @brief Setup Always call this method before each Test-case to ensure
   * correct and new intialization of the used members for a new test case.
   * (If the members are not used in a test, the method does not need to be called).
   */
  void setUp()
  {
    mitk::CovarianceMatrixCalculator::Pointer matrixCalculator
                                      = mitk::CovarianceMatrixCalculator::New();

    m_MovingSurface = mitk::IOUtil::LoadSurface(GetTestDataFilePath("AICPRegistration/head_green.stl"));
    m_FixedSurface = mitk::IOUtil::LoadSurface(GetTestDataFilePath("AICPRegistration/head_red.stl"));

    m_TargetsMovingSurface = mitk::IOUtil::LoadPointSet(GetTestDataFilePath("AICPRegistration/targets_head_green.mps"));
    m_TargetsFixedSurface = mitk::IOUtil::LoadPointSet(GetTestDataFilePath("AICPRegistration/targets_head_red.mps"));

    // compute covariance matrices
    matrixCalculator->SetInputSurface(m_MovingSurface);
    matrixCalculator->ComputeCovarianceMatrices();
    m_SigmasMovingSurface = matrixCalculator->GetCovarianceMatrices();
    const double meanVarX = matrixCalculator->GetMeanVariance();

    matrixCalculator->SetInputSurface(m_FixedSurface);
    matrixCalculator->ComputeCovarianceMatrices();
    m_SigmasFixedSurface = matrixCalculator->GetCovarianceMatrices();
    const double meanVarY = matrixCalculator->GetMeanVariance();

    m_FRENormalizationFactor = sqrt( meanVarX + meanVarY );
  }

  void tearDown()
  {
    m_MovingSurface = NULL;
    m_FixedSurface = NULL;

    m_TargetsMovingSurface = NULL;
    m_TargetsFixedSurface = NULL;

    m_SigmasMovingSurface.clear();
    m_SigmasFixedSurface.clear();
  }

  void testAicpRegistration()
  {
    const double expFRE = 27.5799;
    const double expTRE = 1.68835;
    mitk::AnisotropicIterativeClosestPointRegistration::Pointer aICP =
                      mitk::AnisotropicIterativeClosestPointRegistration::New();

    // set up parameters
    aICP->SetMovingSurface(m_MovingSurface);
    aICP->SetFixedSurface(m_FixedSurface);
    aICP->SetCovarianceMatricesMovingSurface(m_SigmasMovingSurface);
    aICP->SetCovarianceMatricesFixedSurface(m_SigmasFixedSurface);
    aICP->SetFRENormalizationFactor(m_FRENormalizationFactor);
    aICP->SetThreshold(0.000001);

    // run the algorithm
    aICP->Update();

    MITK_INFO << "FRE: Expected: " << expFRE << ", computed: " << aICP->GetFRE();
    CPPUNIT_ASSERT_MESSAGE("mitkAnisotropicIterativeClosestPointRegistrationTest:AicpRegistration Test FRE",
                           mitk::Equal(aICP->GetFRE(),expFRE,0.0001));

    // compute the target registration Error
    const double tre = mitk::AnisotropicRegistrationCommon::ComputeTargetRegistrationError(
                                                                                m_TargetsMovingSurface.GetPointer(),
                                                                                m_TargetsFixedSurface.GetPointer(),
                                                                                aICP->GetRotation(),
                                                                                aICP->GetTranslation()
                                                                                );

   // MITK_INFO << "R:\n" << aICP->GetRotation() << "T: "<< aICP->GetTranslation();

    MITK_INFO << "TRE: Expected: " << expTRE << ", computed: " << tre;
    CPPUNIT_ASSERT_MESSAGE("mitkAnisotropicIterativeClosestPointRegistrationTest:AicpRegistration Test TRE",
                           mitk::Equal(tre,expTRE,0.00001));
  }

  void testTrimmedAicpregistration()
  {
    const double expFRE = 4.8912;
    const double expTRE = 0.0484215;

    mitk::AnisotropicIterativeClosestPointRegistration::Pointer aICP =
                      mitk::AnisotropicIterativeClosestPointRegistration::New();

    // Swap X and Y for partial overlapping registration
    aICP->SetMovingSurface(m_MovingSurface);
    aICP->SetFixedSurface(m_FixedSurface);
    aICP->SetCovarianceMatricesMovingSurface(m_SigmasFixedSurface);
    aICP->SetCovarianceMatricesFixedSurface(m_SigmasMovingSurface);
    aICP->SetFRENormalizationFactor(m_FRENormalizationFactor);
    aICP->SetThreshold(0.000001);
    aICP->SetTrimmFactor(0.50);

    // run the algorithm
    aICP->Update();

    MITK_INFO << "FRE: Expected: " << expFRE << ", computed: " << aICP->GetFRE();

    CPPUNIT_ASSERT_MESSAGE("mitkAnisotropicIterativeClosestPointRegistrationTest:AicpRegistration Test FRE",
                           mitk::Equal(aICP->GetFRE(),expFRE,0.0001));

    // compute the target registration Error
    const double tre = mitk::AnisotropicRegistrationCommon::ComputeTargetRegistrationError(
                                                                                m_TargetsMovingSurface.GetPointer(),
                                                                                m_TargetsFixedSurface.GetPointer(),
                                                                                aICP->GetRotation(),
                                                                                aICP->GetTranslation()
                                                                                );

    MITK_INFO << "TRE: Expected: " << expTRE << ", computed: " << tre;
    CPPUNIT_ASSERT_MESSAGE("mitkAnisotropicIterativeClosestPointRegistrationTest:AicpRegistration Test TRE",
                          mitk::Equal(tre,expTRE,0.00001));
  }
};

MITK_TEST_SUITE_REGISTRATION(mitkAnisotropicIterativeClosestPointRegistration)
<commit_msg>fixed trimmed aicp test<commit_after>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#include <mitkTestingMacros.h>
#include <mitkSurface.h>
#include <mitkIOUtil.h>
#include <vtkCleanPolyData.h>
#include <mitkTestFixture.h>

#include "mitkAnisotropicIterativeClosestPointRegistration.h"
#include "mitkCovarianceMatrixCalculator.h"
#include "mitkAnisotropicRegistrationCommon.h"

/**
 * Test to verify the results of the A-ICP registration.
 * The test runs the standard A-ICP and the trimmed variant.
 */
class mitkAnisotropicIterativeClosestPointRegistrationTestSuite : public mitk::TestFixture
{
  CPPUNIT_TEST_SUITE(mitkAnisotropicIterativeClosestPointRegistrationTestSuite);
  MITK_TEST(testAicpRegistration);
  MITK_TEST(testTrimmedAicpregistration);
  CPPUNIT_TEST_SUITE_END();

private:

  typedef itk::Matrix < double, 3, 3 > Matrix3x3;
  typedef itk::Vector < double, 3 > Vector3;
  typedef std::vector < Matrix3x3 > CovarianceMatrixList;

  mitk::Surface::Pointer m_MovingSurface;
  mitk::Surface::Pointer m_FixedSurface;

  mitk::PointSet::Pointer m_TargetsMovingSurface;
  mitk::PointSet::Pointer m_TargetsFixedSurface;

  CovarianceMatrixList m_SigmasMovingSurface;
  CovarianceMatrixList m_SigmasFixedSurface;

  double m_FRENormalizationFactor;

public:

  /**
   * @brief Setup Always call this method before each Test-case to ensure
   * correct and new intialization of the used members for a new test case.
   * (If the members are not used in a test, the method does not need to be called).
   */
  void setUp()
  {
    mitk::CovarianceMatrixCalculator::Pointer matrixCalculator
                                      = mitk::CovarianceMatrixCalculator::New();

    m_MovingSurface = mitk::IOUtil::LoadSurface(GetTestDataFilePath("AICPRegistration/head_green.stl"));
    m_FixedSurface = mitk::IOUtil::LoadSurface(GetTestDataFilePath("AICPRegistration/head_red.stl"));

    m_TargetsMovingSurface = mitk::IOUtil::LoadPointSet(GetTestDataFilePath("AICPRegistration/targets_head_green.mps"));
    m_TargetsFixedSurface = mitk::IOUtil::LoadPointSet(GetTestDataFilePath("AICPRegistration/targets_head_red.mps"));

    // compute covariance matrices
    matrixCalculator->SetInputSurface(m_MovingSurface);
    matrixCalculator->ComputeCovarianceMatrices();
    m_SigmasMovingSurface = matrixCalculator->GetCovarianceMatrices();
    const double meanVarX = matrixCalculator->GetMeanVariance();

    matrixCalculator->SetInputSurface(m_FixedSurface);
    matrixCalculator->ComputeCovarianceMatrices();
    m_SigmasFixedSurface = matrixCalculator->GetCovarianceMatrices();
    const double meanVarY = matrixCalculator->GetMeanVariance();

    m_FRENormalizationFactor = sqrt( meanVarX + meanVarY );
  }

  void tearDown()
  {
    m_MovingSurface = NULL;
    m_FixedSurface = NULL;

    m_TargetsMovingSurface = NULL;
    m_TargetsFixedSurface = NULL;

    m_SigmasMovingSurface.clear();
    m_SigmasFixedSurface.clear();
  }

  void testAicpRegistration()
  {
    const double expFRE = 27.5799;
    const double expTRE = 1.68835;
    mitk::AnisotropicIterativeClosestPointRegistration::Pointer aICP =
                      mitk::AnisotropicIterativeClosestPointRegistration::New();

    // set up parameters
    aICP->SetMovingSurface(m_MovingSurface);
    aICP->SetFixedSurface(m_FixedSurface);
    aICP->SetCovarianceMatricesMovingSurface(m_SigmasMovingSurface);
    aICP->SetCovarianceMatricesFixedSurface(m_SigmasFixedSurface);
    aICP->SetFRENormalizationFactor(m_FRENormalizationFactor);
    aICP->SetThreshold(0.000001);

    // run the algorithm
    aICP->Update();

    MITK_INFO << "FRE: Expected: " << expFRE << ", computed: " << aICP->GetFRE();
    CPPUNIT_ASSERT_MESSAGE("mitkAnisotropicIterativeClosestPointRegistrationTest:AicpRegistration Test FRE",
                           mitk::Equal(aICP->GetFRE(),expFRE,0.0001));

    // compute the target registration Error
    const double tre = mitk::AnisotropicRegistrationCommon::ComputeTargetRegistrationError(
                                                                                m_TargetsMovingSurface.GetPointer(),
                                                                                m_TargetsFixedSurface.GetPointer(),
                                                                                aICP->GetRotation(),
                                                                                aICP->GetTranslation()
                                                                                );

   // MITK_INFO << "R:\n" << aICP->GetRotation() << "T: "<< aICP->GetTranslation();

    MITK_INFO << "TRE: Expected: " << expTRE << ", computed: " << tre;
    CPPUNIT_ASSERT_MESSAGE("mitkAnisotropicIterativeClosestPointRegistrationTest:AicpRegistration Test TRE",
                           mitk::Equal(tre,expTRE,0.00001));
  }

  void testTrimmedAicpregistration()
  {
    const double expFRE = 4.8912;
    const double expTRE = 0.0484215;

    mitk::AnisotropicIterativeClosestPointRegistration::Pointer aICP =
                      mitk::AnisotropicIterativeClosestPointRegistration::New();

    // Swap X and Y for partial overlapping registration
    aICP->SetMovingSurface(m_MovingSurface);
    aICP->SetFixedSurface(m_FixedSurface);
    aICP->SetCovarianceMatricesMovingSurface(m_SigmasMovingSurface);
    aICP->SetCovarianceMatricesFixedSurface(m_SigmasFixedSurface);
    aICP->SetFRENormalizationFactor(m_FRENormalizationFactor);
    aICP->SetThreshold(0.000001);
    aICP->SetTrimmFactor(0.50);

    // run the algorithm
    aICP->Update();

    MITK_INFO << "FRE: Expected: " << expFRE << ", computed: " << aICP->GetFRE();

    CPPUNIT_ASSERT_MESSAGE("mitkAnisotropicIterativeClosestPointRegistrationTest:AicpRegistration Test FRE",
                           mitk::Equal(aICP->GetFRE(),expFRE,0.0001));

    // compute the target registration Error
    const double tre = mitk::AnisotropicRegistrationCommon::ComputeTargetRegistrationError(
                                                                                m_TargetsMovingSurface.GetPointer(),
                                                                                m_TargetsFixedSurface.GetPointer(),
                                                                                aICP->GetRotation(),
                                                                                aICP->GetTranslation()
                                                                                );

    MITK_INFO << "TRE: Expected: " << expTRE << ", computed: " << tre;
    CPPUNIT_ASSERT_MESSAGE("mitkAnisotropicIterativeClosestPointRegistrationTest:AicpRegistration Test TRE",
                          mitk::Equal(tre,expTRE,0.00001));
  }
};

MITK_TEST_SUITE_REGISTRATION(mitkAnisotropicIterativeClosestPointRegistration)
<|endoftext|>


<commit_before>//===------ macho2yaml.cpp - obj2yaml conversion tool -----------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "Error.h"
#include "obj2yaml.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/ObjectYAML/MachOYAML.h"
#include "llvm/Support/ErrorHandling.h"

#include <string.h> // for memcpy

using namespace llvm;

class MachODumper {

  const object::MachOObjectFile &Obj;

public:
  MachODumper(const object::MachOObjectFile &O) : Obj(O) {}
  Expected<std::unique_ptr<MachOYAML::Object>> dump();
};

#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct)                         \
  case MachO::LCName:                                                          \
    memcpy((void *) & (LC.Data.LCStruct##_data), LoadCmd.Ptr,                  \
           sizeof(MachO::LCStruct));                                           \
    if (Obj.isLittleEndian() != sys::IsLittleEndianHost)                       \
      MachO::swapStruct(LC.Data.LCStruct##_data);                              \
    break;

template <typename SectionType>
MachOYAML::Section constructSection(SectionType Sec) {
  MachOYAML::Section TempSec;
  memcpy(reinterpret_cast<void *>(&TempSec.sectname[0]), &Sec.sectname[0], 16);
  memcpy(reinterpret_cast<void *>(&TempSec.segname[0]), &Sec.segname[0], 16);
  TempSec.addr = Sec.addr;
  TempSec.size = Sec.size;
  TempSec.offset = Sec.offset;
  TempSec.align = Sec.align;
  TempSec.reloff = Sec.reloff;
  TempSec.nreloc = Sec.nreloc;
  TempSec.flags = Sec.flags;
  TempSec.reserved1 = Sec.reserved1;
  TempSec.reserved2 = Sec.reserved2;
  TempSec.reserved3 = 0;
  return TempSec;
}

Expected<std::unique_ptr<MachOYAML::Object>> MachODumper::dump() {
  auto Y = make_unique<MachOYAML::Object>();
  Y->Header.magic = Obj.getHeader().magic;
  Y->Header.cputype = Obj.getHeader().cputype;
  Y->Header.cpusubtype = Obj.getHeader().cpusubtype;
  Y->Header.filetype = Obj.getHeader().filetype;
  Y->Header.ncmds = Obj.getHeader().ncmds;
  Y->Header.sizeofcmds = Obj.getHeader().sizeofcmds;
  Y->Header.flags = Obj.getHeader().flags;
  Y->Header.reserved = 0;

  for (auto LoadCmd : Obj.load_commands()) {
    MachOYAML::LoadCommand LC;
    switch (LoadCmd.C.cmd) {
    default:
      memcpy((void *)&(LC.Data.load_command_data), LoadCmd.Ptr,
             sizeof(MachO::load_command));
      if (Obj.isLittleEndian() != sys::IsLittleEndianHost)
        MachO::swapStruct(LC.Data.load_command_data);
      break;
#include "llvm/Support/MachO.def"
    }
    if (LoadCmd.C.cmd == MachO::LC_SEGMENT) {
      auto End = LoadCmd.Ptr + LoadCmd.C.cmdsize;
      const MachO::section *Curr = reinterpret_cast<const MachO::section *>(
          LoadCmd.Ptr + sizeof(MachO::segment_command));
      for (; reinterpret_cast<const void *>(Curr) < End; Curr++) {
        if (Obj.isLittleEndian() != sys::IsLittleEndianHost) {
          MachO::section Sec;
          memcpy((void *)&Sec, Curr, sizeof(MachO::section));
          MachO::swapStruct(Sec);
          LC.Sections.push_back(constructSection(Sec));
        } else {
          LC.Sections.push_back(constructSection(*Curr));
        }
      }
    } else if (LoadCmd.C.cmd == MachO::LC_SEGMENT_64) {
      auto End = LoadCmd.Ptr + LoadCmd.C.cmdsize;
      const MachO::section_64 *Curr =
          reinterpret_cast<const MachO::section_64 *>(
              LoadCmd.Ptr + sizeof(MachO::segment_command_64));
      for (; reinterpret_cast<const void *>(Curr) < End; Curr++) {
        MachOYAML::Section TempSec;
        if (Obj.isLittleEndian() != sys::IsLittleEndianHost) {
          MachO::section_64 Sec;
          memcpy((void *)&Sec, Curr, sizeof(MachO::section_64));
          MachO::swapStruct(Sec);
          LC.Sections.push_back(constructSection(Sec));
          TempSec = constructSection(Sec);
        } else {
          TempSec = constructSection(*Curr);
        }
        TempSec.reserved3 = Curr->reserved3;
        LC.Sections.push_back(TempSec);
      }
    }
    Y->LoadCommands.push_back(std::move(LC));
  }

  return std::move(Y);
}

Error macho2yaml(raw_ostream &Out, const object::MachOObjectFile &Obj) {
  MachODumper Dumper(Obj);
  Expected<std::unique_ptr<MachOYAML::Object>> YAML = Dumper.dump();
  if (!YAML)
    return YAML.takeError();

  yaml::Output Yout(Out);
  Yout << *(YAML.get());
  return Error::success();
}

Error macho2yaml(raw_ostream &Out, const object::MachOUniversalBinary &Obj) {
  return make_error<Obj2YamlError>(obj2yaml_error::not_implemented);
}

std::error_code macho2yaml(raw_ostream &Out, const object::ObjectFile &Obj) {
  if (const auto *MachOObj = dyn_cast<object::MachOUniversalBinary>(&Obj)) {
    if (auto Err = macho2yaml(Out, *MachOObj)) {
      return errorToErrorCode(std::move(Err));
    }
    return obj2yaml_error::success;
  }

  if (const auto *MachOObj = dyn_cast<object::MachOObjectFile>(&Obj)) {
    if (auto Err = macho2yaml(Out, *MachOObj)) {
      return errorToErrorCode(std::move(Err));
    }
    return obj2yaml_error::success;
  }

  return obj2yaml_error::unsupported_obj_file_format;
}
<commit_msg>[obj2yaml] Refactoring of dumping MachO section structs<commit_after>//===------ macho2yaml.cpp - obj2yaml conversion tool -----------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "Error.h"
#include "obj2yaml.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/ObjectYAML/MachOYAML.h"
#include "llvm/Support/ErrorHandling.h"

#include <string.h> // for memcpy

using namespace llvm;

class MachODumper {

  const object::MachOObjectFile &Obj;

public:
  MachODumper(const object::MachOObjectFile &O) : Obj(O) {}
  Expected<std::unique_ptr<MachOYAML::Object>> dump();
};

#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct)                         \
  case MachO::LCName:                                                          \
    memcpy((void *) & (LC.Data.LCStruct##_data), LoadCmd.Ptr,                  \
           sizeof(MachO::LCStruct));                                           \
    if (Obj.isLittleEndian() != sys::IsLittleEndianHost)                       \
      MachO::swapStruct(LC.Data.LCStruct##_data);                              \
    break;

template <typename SectionType>
MachOYAML::Section constructSectionCommon(SectionType Sec) {
  MachOYAML::Section TempSec;
  memcpy(reinterpret_cast<void *>(&TempSec.sectname[0]), &Sec.sectname[0], 16);
  memcpy(reinterpret_cast<void *>(&TempSec.segname[0]), &Sec.segname[0], 16);
  TempSec.addr = Sec.addr;
  TempSec.size = Sec.size;
  TempSec.offset = Sec.offset;
  TempSec.align = Sec.align;
  TempSec.reloff = Sec.reloff;
  TempSec.nreloc = Sec.nreloc;
  TempSec.flags = Sec.flags;
  TempSec.reserved1 = Sec.reserved1;
  TempSec.reserved2 = Sec.reserved2;
  TempSec.reserved3 = 0;
  return TempSec;
}

template <typename SectionType>
MachOYAML::Section constructSection(SectionType Sec);

template <> MachOYAML::Section constructSection(MachO::section Sec) {
  MachOYAML::Section TempSec = constructSectionCommon(Sec);
  TempSec.reserved3 = 0;
  return TempSec;
}

template <> MachOYAML::Section constructSection(MachO::section_64 Sec) {
  MachOYAML::Section TempSec = constructSectionCommon(Sec);
  TempSec.reserved3 = Sec.reserved3;
  return TempSec;
}

template <typename SectionType, typename SegmentType>
void extractSections(
    const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
    std::vector<MachOYAML::Section> &Sections, bool IsLittleEndian) {
  auto End = LoadCmd.Ptr + LoadCmd.C.cmdsize;
  const SectionType *Curr =
      reinterpret_cast<const SectionType *>(LoadCmd.Ptr + sizeof(SegmentType));
  for (; reinterpret_cast<const void *>(Curr) < End; Curr++) {
    if (IsLittleEndian != sys::IsLittleEndianHost) {
      SectionType Sec;
      memcpy((void *)&Sec, Curr, sizeof(SectionType));
      MachO::swapStruct(Sec);
      Sections.push_back(constructSection(Sec));
    } else {
      Sections.push_back(constructSection(*Curr));
    }
  }
}

Expected<std::unique_ptr<MachOYAML::Object>> MachODumper::dump() {
  auto Y = make_unique<MachOYAML::Object>();
  Y->Header.magic = Obj.getHeader().magic;
  Y->Header.cputype = Obj.getHeader().cputype;
  Y->Header.cpusubtype = Obj.getHeader().cpusubtype;
  Y->Header.filetype = Obj.getHeader().filetype;
  Y->Header.ncmds = Obj.getHeader().ncmds;
  Y->Header.sizeofcmds = Obj.getHeader().sizeofcmds;
  Y->Header.flags = Obj.getHeader().flags;
  Y->Header.reserved = 0;

  for (auto LoadCmd : Obj.load_commands()) {
    MachOYAML::LoadCommand LC;
    switch (LoadCmd.C.cmd) {
    default:
      memcpy((void *)&(LC.Data.load_command_data), LoadCmd.Ptr,
             sizeof(MachO::load_command));
      if (Obj.isLittleEndian() != sys::IsLittleEndianHost)
        MachO::swapStruct(LC.Data.load_command_data);
      break;
#include "llvm/Support/MachO.def"
    }
    switch (LoadCmd.C.cmd) {
    case MachO::LC_SEGMENT:
      extractSections<MachO::section, MachO::segment_command>(
          LoadCmd, LC.Sections, Obj.isLittleEndian());
      break;
    case MachO::LC_SEGMENT_64:
      extractSections<MachO::section_64, MachO::segment_command_64>(
          LoadCmd, LC.Sections, Obj.isLittleEndian());
      break;
    }
    Y->LoadCommands.push_back(std::move(LC));
  }

  return std::move(Y);
}

Error macho2yaml(raw_ostream &Out, const object::MachOObjectFile &Obj) {
  MachODumper Dumper(Obj);
  Expected<std::unique_ptr<MachOYAML::Object>> YAML = Dumper.dump();
  if (!YAML)
    return YAML.takeError();

  yaml::Output Yout(Out);
  Yout << *(YAML.get());
  return Error::success();
}

Error macho2yaml(raw_ostream &Out, const object::MachOUniversalBinary &Obj) {
  return make_error<Obj2YamlError>(obj2yaml_error::not_implemented);
}

std::error_code macho2yaml(raw_ostream &Out, const object::ObjectFile &Obj) {
  if (const auto *MachOObj = dyn_cast<object::MachOUniversalBinary>(&Obj)) {
    if (auto Err = macho2yaml(Out, *MachOObj)) {
      return errorToErrorCode(std::move(Err));
    }
    return obj2yaml_error::success;
  }

  if (const auto *MachOObj = dyn_cast<object::MachOObjectFile>(&Obj)) {
    if (auto Err = macho2yaml(Out, *MachOObj)) {
      return errorToErrorCode(std::move(Err));
    }
    return obj2yaml_error::success;
  }

  return obj2yaml_error::unsupported_obj_file_format;
}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 1992-$THISYEAR$ $TROLLTECH$. All rights reserved.
**
** This file is part of the $MODULE$ of the Qt Toolkit.
**
** $TROLLTECH_DUAL_LICENSE$
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/

#include <qfxtester.h>
#include <QDebug>
#include <QApplication>
#include <qmlview.h>
#include <QFile>
#include <QmlComponent>
#include <QDir>
#include <QCryptographicHash>
#include <private/qabstractanimation_p.h>
#include <private/qfxitem_p.h>

QT_BEGIN_NAMESPACE

QML_DEFINE_TYPE(Qt.VisualTest, 4, 6, (QT_VERSION&0x00ff00)>>8, VisualTest, QFxVisualTest);
QML_DEFINE_TYPE(Qt.VisualTest, 4, 6, (QT_VERSION&0x00ff00)>>8, Frame, QFxVisualTestFrame);
QML_DEFINE_TYPE(Qt.VisualTest, 4, 6, (QT_VERSION&0x00ff00)>>8, Mouse, QFxVisualTestMouse);
QML_DEFINE_TYPE(Qt.VisualTest, 4, 6, (QT_VERSION&0x00ff00)>>8, Key, QFxVisualTestKey);

QFxTester::QFxTester(const QString &script, QmlViewer::ScriptOptions opts,
                     QmlView *parent)
: QAbstractAnimation(parent), m_script(script), m_view(parent), filterEvents(true), options(opts),
  testscript(0), hasCompleted(false), hasFailed(false)
{
    parent->viewport()->installEventFilter(this);
    parent->installEventFilter(this);
    QUnifiedTimer::instance()->setConsistentTiming(true);
    if (options & QmlViewer::Play)
        this->run();
    start();
}

QFxTester::~QFxTester()
{
    if (!hasFailed &&
        options & QmlViewer::Record &&
        options & QmlViewer::SaveOnExit)
        save();
}

int QFxTester::duration() const
{
    return -1;
}

void QFxTester::addMouseEvent(Destination dest, QMouseEvent *me)
{
    MouseEvent e(me);
    e.destination = dest;
    m_mouseEvents << e;
}

void QFxTester::addKeyEvent(Destination dest, QKeyEvent *ke)
{
    KeyEvent e(ke);
    e.destination = dest;
    m_keyEvents << e;
}

bool QFxTester::eventFilter(QObject *o, QEvent *e)
{
    if (!filterEvents)
        return false;

    Destination destination;
    if (o == m_view) {
        destination = View;
    } else if (o == m_view->viewport()) {
        destination = ViewPort;
    } else {
        return false;
    }

    switch (e->type()) {
        case QEvent::KeyPress:
        case QEvent::KeyRelease:
            addKeyEvent(destination, (QKeyEvent *)e);
            return true;
        case QEvent::MouseButtonPress:
        case QEvent::MouseButtonRelease:
        case QEvent::MouseMove:
        case QEvent::MouseButtonDblClick:
            addMouseEvent(destination, (QMouseEvent *)e);
            return true;
        default:
            break;
    }
    return false;
}

void QFxTester::executefailure()
{
    hasFailed = true;

    if (options & QmlViewer::ExitOnFailure)
        exit(-1);
}

void QFxTester::imagefailure()
{
    hasFailed = true;

    if (options & QmlViewer::ExitOnFailure)
        exit(-1);
}

void QFxTester::complete()
{
    if (hasCompleted)
        return;
    hasCompleted = true;
    if (options & QmlViewer::ExitOnComplete)
        QApplication::exit(hasFailed?-1:0);
    else if (options & QmlViewer::Play)
        qWarning("Script playback complete");
}

void QFxTester::run()
{
    QmlComponent c(m_view->engine(), m_script + QLatin1String(".qml"));

    testscript = qobject_cast<QFxVisualTest *>(c.create());
    if (testscript) testscript->setParent(this);
    else executefailure();
    testscriptidx = 0;
}

void QFxTester::save()
{
    QString filename = m_script + QLatin1String(".qml");
    QDir saveDir = QFileInfo(filename).absoluteDir();
    saveDir.mkpath(".");

    QFile file(filename);
    file.open(QIODevice::WriteOnly);
    QTextStream ts(&file);

    ts << "import Qt.VisualTest 4.6\n\n";
    ts << "VisualTest {\n";

    int imgCount = 0;
    QList<KeyEvent> keyevents = m_savedKeyEvents;
    QList<MouseEvent> mouseevents = m_savedMouseEvents;
    for (int ii = 0; ii < m_savedFrameEvents.count(); ++ii) {
        const FrameEvent &fe = m_savedFrameEvents.at(ii);
        ts << "    Frame {\n";
        ts << "        msec: " << fe.msec << "\n";
        if (!fe.hash.isEmpty()) {
            ts << "        hash: \"" << fe.hash.toHex() << "\"\n";
        } else if (!fe.image.isNull()) {
            QString filename = m_script + "." + QString::number(imgCount++) + ".png";
            fe.image.save(filename);
            ts << "        image: \"" << filename << "\"\n";
        }
        ts << "    }\n";

        while (!mouseevents.isEmpty() &&
               mouseevents.first().msec == fe.msec) {
            MouseEvent me = mouseevents.takeFirst();

            ts << "    Mouse {\n";
            ts << "        type: " << me.type << "\n";
            ts << "        button: " << me.button << "\n";
            ts << "        buttons: " << me.buttons << "\n";
            ts << "        x: " << me.pos.x() << "; y: " << me.pos.y() << "\n";
            ts << "        modifiers: " << me.modifiers << "\n";
            if (me.destination == ViewPort)
                ts << "        sendToViewport: true\n";
            ts << "    }\n";
        }

        while (!keyevents.isEmpty() &&
               keyevents.first().msec == fe.msec) {
            KeyEvent ke = keyevents.takeFirst();

            ts << "    Key {\n";
            ts << "        type: " << ke.type << "\n";
            ts << "        key: " << ke.key << "\n";
            ts << "        modifiers: " << ke.modifiers << "\n";
            ts << "        text: \"" << ke.text.toUtf8().toHex() << "\"\n";
            ts << "        autorep: " << (ke.autorep?"true":"false") << "\n";
            ts << "        count: " << ke.count << "\n";
            if (ke.destination == ViewPort)
                ts << "        sendToViewport: true\n";
            ts << "    }\n";
        }
    }

    ts << "}\n";
    file.close();
}

void QFxTester::updateCurrentTime(int msec)
{
    QFxItemPrivate::setConsistentTime(msec);

    QImage img(m_view->width(), m_view->height(), QImage::Format_RGB32);

    QPainter p(&img);
    m_view->render(&p);

    FrameEvent fe;
    fe.msec = msec;
    if (msec == 0) {
        // Skip first frame
    } else if (0 == (m_savedFrameEvents.count() % 60)) {
        fe.image = img;
    } else {
        QCryptographicHash hash(QCryptographicHash::Md5);
        hash.addData((const char *)img.bits(), img.bytesPerLine() * img.height());
        fe.hash = hash.result();
    }
    m_savedFrameEvents.append(fe);

    // Deliver mouse events
    filterEvents = false;

    if (!testscript) {
        for (int ii = 0; ii < m_mouseEvents.count(); ++ii) {
            MouseEvent &me = m_mouseEvents[ii];
            me.msec = msec;
            QMouseEvent event(me.type, me.pos, me.button, me.buttons, me.modifiers);

            if (me.destination == View) {
                QCoreApplication::sendEvent(m_view, &event);
            } else {
                QCoreApplication::sendEvent(m_view->viewport(), &event);
            }
        }

        for (int ii = 0; ii < m_keyEvents.count(); ++ii) {
            KeyEvent &ke = m_keyEvents[ii];
            ke.msec = msec;
            QKeyEvent event(ke.type, ke.key, ke.modifiers, ke.text, ke.autorep, ke.count);

            if (ke.destination == View) {
                QCoreApplication::sendEvent(m_view, &event);
            } else {
                QCoreApplication::sendEvent(m_view->viewport(), &event);
            }
        }
        m_savedMouseEvents.append(m_mouseEvents);
        m_savedKeyEvents.append(m_keyEvents);
    }

    m_mouseEvents.clear();
    m_keyEvents.clear();

    // Advance test script
    static int imgCount = 0;
    while (testscript && testscript->count() > testscriptidx) {

        QObject *event = testscript->event(testscriptidx);

        if (QFxVisualTestFrame *frame = qobject_cast<QFxVisualTestFrame *>(event)) {
            if (frame->msec() < msec) {
                if (options & QmlViewer::TestImages) {
                    qWarning() << "QFxTester: Extra frame.  Seen:"
                               << msec << "Expected:" << frame->msec();
                    imagefailure();
                }
            } else if (frame->msec() == msec) {
                if (frame->hash().toUtf8() != fe.hash.toHex()) {
                    if (options & QmlViewer::TestImages) {
                        qWarning() << "QFxTester: Mismatched frame hash.  Seen:"
                                   << fe.hash.toHex() << "Expected:"
                                   << frame->hash().toUtf8();
                        imagefailure();
                    }
                }
            } else if (frame->msec() > msec) {
                break;
            }

            if (options & QmlViewer::TestImages && !frame->image().isEmpty()) {
                QImage goodImage(frame->image().toLocalFile());
                if (goodImage != img) {
                    QString reject(frame->image().toLocalFile() + ".reject.png");
                    qWarning() << "QFxTester: Image mismatch.  Reject saved to:"
                               << reject;
                    img.save(reject);
                    imagefailure();
                }
            }
        } else if (QFxVisualTestMouse *mouse = qobject_cast<QFxVisualTestMouse *>(event)) {
            QPoint pos(mouse->x(), mouse->y());
            QPoint globalPos = m_view->mapToGlobal(QPoint(0, 0)) + pos;
            QMouseEvent event((QEvent::Type)mouse->type(), pos, globalPos, (Qt::MouseButton)mouse->button(), (Qt::MouseButtons)mouse->buttons(), (Qt::KeyboardModifiers)mouse->modifiers());

            MouseEvent me(&event);
            me.msec = msec;
            if (!mouse->sendToViewport()) {
                QCoreApplication::sendEvent(m_view, &event);
                me.destination = View;
            } else {
                QCoreApplication::sendEvent(m_view->viewport(), &event);
                me.destination = ViewPort;
            }
            m_savedMouseEvents.append(me);
        } else if (QFxVisualTestKey *key = qobject_cast<QFxVisualTestKey *>(event)) {

            QKeyEvent event((QEvent::Type)key->type(), key->key(), (Qt::KeyboardModifiers)key->modifiers(), QString::fromUtf8(QByteArray::fromHex(key->text().toUtf8())), key->autorep(), key->count());

            KeyEvent ke(&event);
            ke.msec = msec;
            if (!key->sendToViewport()) {
                QCoreApplication::sendEvent(m_view, &event);
                ke.destination = View;
            } else {
                QCoreApplication::sendEvent(m_view->viewport(), &event);
                ke.destination = ViewPort;
            }
            m_savedKeyEvents.append(ke);
        }
        testscriptidx++;
    }

    filterEvents = true;

    if (testscript && testscript->count() <= testscriptidx)
        complete();
}

QT_END_NAMESPACE
<commit_msg>Fix relative-URLs of test scripts<commit_after>/****************************************************************************
**
** Copyright (C) 1992-$THISYEAR$ $TROLLTECH$. All rights reserved.
**
** This file is part of the $MODULE$ of the Qt Toolkit.
**
** $TROLLTECH_DUAL_LICENSE$
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/

#include <qfxtester.h>
#include <QDebug>
#include <QApplication>
#include <qmlview.h>
#include <QFile>
#include <QmlComponent>
#include <QDir>
#include <QCryptographicHash>
#include <private/qabstractanimation_p.h>
#include <private/qfxitem_p.h>

QT_BEGIN_NAMESPACE

QML_DEFINE_TYPE(Qt.VisualTest, 4, 6, (QT_VERSION&0x00ff00)>>8, VisualTest, QFxVisualTest);
QML_DEFINE_TYPE(Qt.VisualTest, 4, 6, (QT_VERSION&0x00ff00)>>8, Frame, QFxVisualTestFrame);
QML_DEFINE_TYPE(Qt.VisualTest, 4, 6, (QT_VERSION&0x00ff00)>>8, Mouse, QFxVisualTestMouse);
QML_DEFINE_TYPE(Qt.VisualTest, 4, 6, (QT_VERSION&0x00ff00)>>8, Key, QFxVisualTestKey);

QFxTester::QFxTester(const QString &script, QmlViewer::ScriptOptions opts,
                     QmlView *parent)
: QAbstractAnimation(parent), m_script(script), m_view(parent), filterEvents(true), options(opts),
  testscript(0), hasCompleted(false), hasFailed(false)
{
    parent->viewport()->installEventFilter(this);
    parent->installEventFilter(this);
    QUnifiedTimer::instance()->setConsistentTiming(true);
    if (options & QmlViewer::Play)
        this->run();
    start();
}

QFxTester::~QFxTester()
{
    if (!hasFailed &&
        options & QmlViewer::Record &&
        options & QmlViewer::SaveOnExit)
        save();
}

int QFxTester::duration() const
{
    return -1;
}

void QFxTester::addMouseEvent(Destination dest, QMouseEvent *me)
{
    MouseEvent e(me);
    e.destination = dest;
    m_mouseEvents << e;
}

void QFxTester::addKeyEvent(Destination dest, QKeyEvent *ke)
{
    KeyEvent e(ke);
    e.destination = dest;
    m_keyEvents << e;
}

bool QFxTester::eventFilter(QObject *o, QEvent *e)
{
    if (!filterEvents)
        return false;

    Destination destination;
    if (o == m_view) {
        destination = View;
    } else if (o == m_view->viewport()) {
        destination = ViewPort;
    } else {
        return false;
    }

    switch (e->type()) {
        case QEvent::KeyPress:
        case QEvent::KeyRelease:
            addKeyEvent(destination, (QKeyEvent *)e);
            return true;
        case QEvent::MouseButtonPress:
        case QEvent::MouseButtonRelease:
        case QEvent::MouseMove:
        case QEvent::MouseButtonDblClick:
            addMouseEvent(destination, (QMouseEvent *)e);
            return true;
        default:
            break;
    }
    return false;
}

void QFxTester::executefailure()
{
    hasFailed = true;

    if (options & QmlViewer::ExitOnFailure)
        exit(-1);
}

void QFxTester::imagefailure()
{
    hasFailed = true;

    if (options & QmlViewer::ExitOnFailure)
        exit(-1);
}

void QFxTester::complete()
{
    if (hasCompleted)
        return;
    hasCompleted = true;
    if (options & QmlViewer::ExitOnComplete)
        QApplication::exit(hasFailed?-1:0);
    else if (options & QmlViewer::Play)
        qWarning("Script playback complete");
}

void QFxTester::run()
{
    QmlComponent c(m_view->engine(), m_script + QLatin1String(".qml"));

    testscript = qobject_cast<QFxVisualTest *>(c.create());
    if (testscript) testscript->setParent(this);
    else executefailure();
    testscriptidx = 0;
}

void QFxTester::save()
{
    QString filename = m_script + QLatin1String(".qml");
    QFileInfo filenameInfo(filename);
    QDir saveDir = filenameInfo.absoluteDir();
    saveDir.mkpath(".");

    QFile file(filename);
    file.open(QIODevice::WriteOnly);
    QTextStream ts(&file);

    ts << "import Qt.VisualTest 4.6\n\n";
    ts << "VisualTest {\n";

    int imgCount = 0;
    QList<KeyEvent> keyevents = m_savedKeyEvents;
    QList<MouseEvent> mouseevents = m_savedMouseEvents;
    for (int ii = 0; ii < m_savedFrameEvents.count(); ++ii) {
        const FrameEvent &fe = m_savedFrameEvents.at(ii);
        ts << "    Frame {\n";
        ts << "        msec: " << fe.msec << "\n";
        if (!fe.hash.isEmpty()) {
            ts << "        hash: \"" << fe.hash.toHex() << "\"\n";
        } else if (!fe.image.isNull()) {
            QString filename = filenameInfo.baseName() + "." + QString::number(imgCount++) + ".png";
            fe.image.save(filename);
            ts << "        image: \"" << filename << "\"\n";
        }
        ts << "    }\n";

        while (!mouseevents.isEmpty() &&
               mouseevents.first().msec == fe.msec) {
            MouseEvent me = mouseevents.takeFirst();

            ts << "    Mouse {\n";
            ts << "        type: " << me.type << "\n";
            ts << "        button: " << me.button << "\n";
            ts << "        buttons: " << me.buttons << "\n";
            ts << "        x: " << me.pos.x() << "; y: " << me.pos.y() << "\n";
            ts << "        modifiers: " << me.modifiers << "\n";
            if (me.destination == ViewPort)
                ts << "        sendToViewport: true\n";
            ts << "    }\n";
        }

        while (!keyevents.isEmpty() &&
               keyevents.first().msec == fe.msec) {
            KeyEvent ke = keyevents.takeFirst();

            ts << "    Key {\n";
            ts << "        type: " << ke.type << "\n";
            ts << "        key: " << ke.key << "\n";
            ts << "        modifiers: " << ke.modifiers << "\n";
            ts << "        text: \"" << ke.text.toUtf8().toHex() << "\"\n";
            ts << "        autorep: " << (ke.autorep?"true":"false") << "\n";
            ts << "        count: " << ke.count << "\n";
            if (ke.destination == ViewPort)
                ts << "        sendToViewport: true\n";
            ts << "    }\n";
        }
    }

    ts << "}\n";
    file.close();
}

void QFxTester::updateCurrentTime(int msec)
{
    QFxItemPrivate::setConsistentTime(msec);

    QImage img(m_view->width(), m_view->height(), QImage::Format_RGB32);

    QPainter p(&img);
    m_view->render(&p);

    FrameEvent fe;
    fe.msec = msec;
    if (msec == 0) {
        // Skip first frame
    } else if (0 == (m_savedFrameEvents.count() % 60)) {
        fe.image = img;
    } else {
        QCryptographicHash hash(QCryptographicHash::Md5);
        hash.addData((const char *)img.bits(), img.bytesPerLine() * img.height());
        fe.hash = hash.result();
    }
    m_savedFrameEvents.append(fe);

    // Deliver mouse events
    filterEvents = false;

    if (!testscript) {
        for (int ii = 0; ii < m_mouseEvents.count(); ++ii) {
            MouseEvent &me = m_mouseEvents[ii];
            me.msec = msec;
            QMouseEvent event(me.type, me.pos, me.button, me.buttons, me.modifiers);

            if (me.destination == View) {
                QCoreApplication::sendEvent(m_view, &event);
            } else {
                QCoreApplication::sendEvent(m_view->viewport(), &event);
            }
        }

        for (int ii = 0; ii < m_keyEvents.count(); ++ii) {
            KeyEvent &ke = m_keyEvents[ii];
            ke.msec = msec;
            QKeyEvent event(ke.type, ke.key, ke.modifiers, ke.text, ke.autorep, ke.count);

            if (ke.destination == View) {
                QCoreApplication::sendEvent(m_view, &event);
            } else {
                QCoreApplication::sendEvent(m_view->viewport(), &event);
            }
        }
        m_savedMouseEvents.append(m_mouseEvents);
        m_savedKeyEvents.append(m_keyEvents);
    }

    m_mouseEvents.clear();
    m_keyEvents.clear();

    // Advance test script
    static int imgCount = 0;
    while (testscript && testscript->count() > testscriptidx) {

        QObject *event = testscript->event(testscriptidx);

        if (QFxVisualTestFrame *frame = qobject_cast<QFxVisualTestFrame *>(event)) {
            if (frame->msec() < msec) {
                if (options & QmlViewer::TestImages) {
                    qWarning() << "QFxTester: Extra frame.  Seen:"
                               << msec << "Expected:" << frame->msec();
                    imagefailure();
                }
            } else if (frame->msec() == msec) {
                if (frame->hash().toUtf8() != fe.hash.toHex()) {
                    if (options & QmlViewer::TestImages) {
                        qWarning() << "QFxTester: Mismatched frame hash.  Seen:"
                                   << fe.hash.toHex() << "Expected:"
                                   << frame->hash().toUtf8();
                        imagefailure();
                    }
                }
            } else if (frame->msec() > msec) {
                break;
            }

            if (options & QmlViewer::TestImages && !frame->image().isEmpty()) {
                QImage goodImage(frame->image().toLocalFile());
                if (goodImage != img) {
                    QString reject(frame->image().toLocalFile() + ".reject.png");
                    qWarning() << "QFxTester: Image mismatch.  Reject saved to:"
                               << reject;
                    img.save(reject);
                    imagefailure();
                }
            }
        } else if (QFxVisualTestMouse *mouse = qobject_cast<QFxVisualTestMouse *>(event)) {
            QPoint pos(mouse->x(), mouse->y());
            QPoint globalPos = m_view->mapToGlobal(QPoint(0, 0)) + pos;
            QMouseEvent event((QEvent::Type)mouse->type(), pos, globalPos, (Qt::MouseButton)mouse->button(), (Qt::MouseButtons)mouse->buttons(), (Qt::KeyboardModifiers)mouse->modifiers());

            MouseEvent me(&event);
            me.msec = msec;
            if (!mouse->sendToViewport()) {
                QCoreApplication::sendEvent(m_view, &event);
                me.destination = View;
            } else {
                QCoreApplication::sendEvent(m_view->viewport(), &event);
                me.destination = ViewPort;
            }
            m_savedMouseEvents.append(me);
        } else if (QFxVisualTestKey *key = qobject_cast<QFxVisualTestKey *>(event)) {

            QKeyEvent event((QEvent::Type)key->type(), key->key(), (Qt::KeyboardModifiers)key->modifiers(), QString::fromUtf8(QByteArray::fromHex(key->text().toUtf8())), key->autorep(), key->count());

            KeyEvent ke(&event);
            ke.msec = msec;
            if (!key->sendToViewport()) {
                QCoreApplication::sendEvent(m_view, &event);
                ke.destination = View;
            } else {
                QCoreApplication::sendEvent(m_view->viewport(), &event);
                ke.destination = ViewPort;
            }
            m_savedKeyEvents.append(ke);
        }
        testscriptidx++;
    }

    filterEvents = true;

    if (testscript && testscript->count() <= testscriptidx)
        complete();
}

QT_END_NAMESPACE
<|endoftext|>