<commit_before>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#pragma once

#ifndef NDEBUG
#include <iostream>
#endif

#include <visionaray/gl/util.h>

namespace visionaray
{

//-------------------------------------------------------------------------------------------------
// cpu_buffer_rt
//

template <pixel_format CF, pixel_format DF>
typename cpu_buffer_rt<CF, DF>::color_type* cpu_buffer_rt<CF, DF>::color()
{
    return color_buffer_.data();
}

template <pixel_format CF, pixel_format DF>
typename cpu_buffer_rt<CF, DF>::depth_type* cpu_buffer_rt<CF, DF>::depth()
{
    return depth_buffer_.data();
}

template <pixel_format CF, pixel_format DF>
typename cpu_buffer_rt<CF, DF>::color_type const* cpu_buffer_rt<CF, DF>::color() const
{
    return color_buffer_.data();
}

template <pixel_format CF, pixel_format DF>
typename cpu_buffer_rt<CF, DF>::depth_type const* cpu_buffer_rt<CF, DF>::depth() const
{
    return depth_buffer_.data();
}

template <pixel_format CF, pixel_format DF>
void cpu_buffer_rt<CF, DF>::begin_frame()
{
}

template <pixel_format CF, pixel_format DF>
void cpu_buffer_rt<CF, DF>::end_frame()
{
}

template <pixel_format CF, pixel_format DF>
void cpu_buffer_rt<CF, DF>::resize(size_t w, size_t h)
{
    pixel_format_info cinfo = map_pixel_format(color_traits::format);
    color_buffer_.resize( w * h * cinfo.size );

    if (depth_traits::format != PF_UNSPECIFIED)
    {
        pixel_format_info dinfo = map_pixel_format(depth_traits::format);
        depth_buffer_.resize( w * h * dinfo.size );
    }

    render_target::resize(w, h);
}

template <pixel_format CF, pixel_format DF>
void cpu_buffer_rt<CF, DF>::display_color_buffer() const
{

    pixel_format_info info = map_pixel_format(color_traits::format);
    gl::blend_pixels( width(), height(), info.format, info.type, color_buffer_.data() );

}

} // visionaray
<commit_msg>Depth-compositing with cpu_buffer_rt<commit_after>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#pragma once

#ifndef NDEBUG
#include <iostream>
#endif

#include <visionaray/gl/util.h>

namespace visionaray
{

//-------------------------------------------------------------------------------------------------
// cpu_buffer_rt
//

template <pixel_format CF, pixel_format DF>
typename cpu_buffer_rt<CF, DF>::color_type* cpu_buffer_rt<CF, DF>::color()
{
    return color_buffer_.data();
}

template <pixel_format CF, pixel_format DF>
typename cpu_buffer_rt<CF, DF>::depth_type* cpu_buffer_rt<CF, DF>::depth()
{
    return depth_buffer_.data();
}

template <pixel_format CF, pixel_format DF>
typename cpu_buffer_rt<CF, DF>::color_type const* cpu_buffer_rt<CF, DF>::color() const
{
    return color_buffer_.data();
}

template <pixel_format CF, pixel_format DF>
typename cpu_buffer_rt<CF, DF>::depth_type const* cpu_buffer_rt<CF, DF>::depth() const
{
    return depth_buffer_.data();
}

template <pixel_format CF, pixel_format DF>
void cpu_buffer_rt<CF, DF>::begin_frame()
{
}

template <pixel_format CF, pixel_format DF>
void cpu_buffer_rt<CF, DF>::end_frame()
{
}

template <pixel_format CF, pixel_format DF>
void cpu_buffer_rt<CF, DF>::resize(size_t w, size_t h)
{
    pixel_format_info cinfo = map_pixel_format(color_traits::format);
    color_buffer_.resize( w * h * cinfo.size );

    if (depth_traits::format != PF_UNSPECIFIED)
    {
        pixel_format_info dinfo = map_pixel_format(depth_traits::format);
        depth_buffer_.resize( w * h * dinfo.size );
    }

    render_target::resize(w, h);
}

template <pixel_format CF, pixel_format DF>
void cpu_buffer_rt<CF, DF>::display_color_buffer() const
{

    if (depth_traits::format != PF_UNSPECIFIED)
    {
        glPushAttrib( GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_ENABLE_BIT );

        glEnable(GL_STENCIL_TEST);
        glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
        glStencilFunc(GL_ALWAYS, 1, 1);
        glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

        pixel_format_info dinfo = map_pixel_format(depth_traits::format);
        gl::blend_pixels( width(), height(), dinfo.format, dinfo.type, depth_buffer_.data() );

        glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
        glStencilFunc(GL_EQUAL, 1, 1);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        glDisable(GL_DEPTH_TEST);

        pixel_format_info cinfo = map_pixel_format(color_traits::format);
        gl::blend_pixels( width(), height(), cinfo.format, cinfo.type, color_buffer_.data() );

        glPopAttrib();
    }
    else
    {
        pixel_format_info info = map_pixel_format(color_traits::format);
        gl::blend_pixels( width(), height(), info.format, info.type, color_buffer_.data() );
    }

}

} // visionaray
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2005, Peter Sommerlad and IFS Institute for Software at HSR Rapperswil, Switzerland
 * All rights reserved.
 *
 * This library/application is free software; you can redistribute and/or modify it under the terms of
 * the license that is included with this library/application in the file license.txt.
 */

//--- interface include --------------------------------------------------------
#include "SysLog.h"
#if defined(WIN32)
#define IOSTREAM_IS_THREADSAFE
#endif

//--- standard modules used ----------------------------------------------------
#include "System.h"

//--- c-library modules used ---------------------------------------------------
#include <errno.h>
#include <cstdio>
#include <cstring>
#if defined(__sun) || defined(__linux__) || defined(__aix__)
#include <syslog.h>
#endif
#if defined(__SUNPRO_CC)
#include <strings.h>
#include <stdio.h>
#endif

//--- SysLog ----------------------------------------------------------
SysLog *SysLog::fgSysLog = 0;
SysLog::eLogLevel SysLog::fgDoSystemLevelLog = SysLog::eALERT;
SysLog::eLogLevel SysLog::fgDoLogOnCerr = SysLog::eERR;

//--- module initialization/termination
void SysLog::Init(const char *appId)
{
	// beware this is not thread safe
	// the syslog channel is initialized
	// by the main thread in the server constructor
	// but since logging takes place before the
	// server object exists we have to take care

	if ( appId ) {
		Terminate();    // close existing syslog channel with default Coast name
	} else {
		appId = "Coast";
	}

	String strValue = System::EnvGet("WD_DOLOG");
	int iValue = (int)strValue.AsLong(eNone);
	if ( ( eNone < iValue ) && ( iValue < eLast ) ) {
		fgDoSystemLevelLog = (eLogLevel)iValue;
	}
	strValue = System::EnvGet("WD_LOGONCERR");
	iValue = (int)strValue.AsLong(eNone);
	if ( ( eNone < iValue ) && ( iValue < eLast ) ) {
		fgDoLogOnCerr = (eLogLevel)iValue;
	}

	// initialize the syslog channel
	if ( !fgSysLog ) {
		// open the syslog channel for this application
		// there is always only one syslog channel per application
#if defined(__sun) || defined(__linux__) || defined(__aix__)
		fgSysLog = new UnixSysLog(appId);
#endif
#if defined(WIN32)
		fgSysLog = new Win32SysLog(appId);
#endif
#if defined(__370__)
		fgSysLog = new S370SysLog();
#endif
	}
}

void SysLog::Terminate()
{
	if ( fgSysLog ) {
		delete fgSysLog;
		fgSysLog = 0;
	}
}

//--- logging API -------------------------------------

// severity eDEBUG for tracing server
// activity during development and
// deployment
void SysLog::Debug(const char *msg)
{
	Log(eDEBUG, msg);
}

// severity eINFO for general information
// log general useful information about server
// activity
void SysLog::Info(const char *msg)
{
	Log(eINFO, msg);
}

// severity eWARNING	for information about
// inconsistent state or potential dangerous
// situation
void SysLog::Warning(const char *msg)
{
	Log(eWARNING, msg);
}

// severity eERR for outright errors
// during operation of the server without
// fatal results
void SysLog::Error(const char *msg)
{
	Log(eERR, msg);
}

// severity eALERT for fatal errors
// this call triggers also an alert on
// the operator console
void SysLog::Alert(const char *msg)
{
	Log(eALERT, msg);
}

// bottleneck routine used by others
// here you can use severity levels directly
void SysLog::Log(eLogLevel level, const char *msg)
{
	if ( InitOnce() ) {
		fgSysLog->DoLog(level, msg);
	}
}

bool SysLog::InitOnce()
{
	static bool once = false;
	if (!once) {
		once = true;
		Init(0);
	}
	return (fgSysLog != NULL);
}

//--- utilities ------------------------------------

// const char *SysErrorMsg(long errnum) maps the error number in errnum
// to an error  message  string,  and returns a pointer to that string.
// SysErrorMsg(long errnum) uses the same set of error messages as perror().
// The returned string should not be overwritten.
String SysLog::SysErrorMsg(long errnum)
{
#if defined(WIN32)
	LPVOID lpMsgBuf;

	FormatMessage(
		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
		NULL,
		errnum,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
		(LPTSTR) &lpMsgBuf,
		0,
		NULL
	);

	String msg((const char *)lpMsgBuf, strlen(lpMsgBuf), Storage::Global());
	// Free the buffer.
	LocalFree( lpMsgBuf );
	return msg;
#else
	String theError(Storage::Global());
	if (errnum == EMFILE) {
		theError = "[No more filehandles in Process.] ";
	}
	if (errnum == ENFILE) {
		theError = "[No more filehandles in System.] ";
	}
	theError.Append((long)errnum).Append(' ').Append(strerror(errnum));
	return theError;
#endif
}

// const char *LastSysError() returns the error message retrieved with errno
// from SysErrorMsg(errno)
String SysLog::LastSysError()
{
	int iError( System::GetSystemError() );
	if ( iError != 0 ) {
		return SysErrorMsg( iError );
	}
	return String().Append((long)iError).Append(": no system-error");
}

//magic function to be used by Assert macro to avoid dependency to class SysLog and SysLog.h
int syslog_assert(const char *file, long line, const char *assertion)
{
	return SysLog::LogAssert(file, line, assertion);
}

int SysLog::LogAssert(const char *file, long line, const char *assertion)
{
	// implement brain dead with C style things to avoid problems of recursion
	char asrt_buf[2048];
	int asrt_buf_used = sprintf(asrt_buf, "%s:%ld\n Assert(%s) failed\n", file, line, assertion);
	\
	SysLog::WriteToStderr(asrt_buf, asrt_buf_used);
	return 0;
}

void SysLog::WriteToStderr(const String &msg)
{
	SysLog::WriteToStderr((const char *)msg, msg.Length());
}

void SysLog::WriteToStderr(char *msg, long length)
{
	SysLog::WriteToStderr((const char *)msg, length);
}

void SysLog::WriteToStderr(const char *msg, long length)
{
	if ( msg ) {
		long sLen = length;
		if ( sLen < 0 ) {
			sLen = strlen(msg);
		}
#ifdef IOSTREAM_IS_THREADSAFE
		cerr.write(msg, sLen).flush();
#else
		::write(2, msg, sLen);
#endif
	}
}

void SysLog::WriteToStdout(const String &msg)
{
	SysLog::WriteToStdout((const char *)msg, msg.Length());
}

void SysLog::WriteToStdout(char *msg, long length)
{
	SysLog::WriteToStdout((const char *)msg, length);
}

void SysLog::WriteToStdout(const char *msg, long length)
{
	if ( msg ) {
		long sLen = length;
		if ( sLen < 0 ) {
			sLen = strlen(msg);
		}
#ifdef IOSTREAM_IS_THREADSAFE
		cout.write(msg, sLen).flush();
#else
		::write(1, msg, sLen);
#endif
	}
}

SysLog::SysLog()
{
}

SysLog::~SysLog()
{
}

void SysLog::DoLog(eLogLevel level, const char *msg)
{
	// override logging parameter if it is an alert message
	if ( level >= fgDoSystemLevelLog ) {
		DoSystemLevelLog(level, msg);
	}
	// override logging parameter if it is an error or alert message
	if ( level >= fgDoLogOnCerr ) {
		DoLogTrace(level, msg);
	}
}

void SysLog::DoTraceLevel(const char *level, const char *msg)
{
	String finalMessage(level, strlen(level), Storage::Global());
	finalMessage.Append(msg);
	finalMessage.Append('\n');
	SysLog::WriteToStderr(finalMessage, finalMessage.Length());
}

void SysLog::DoLogTrace(eLogLevel level, const char *logMsg)
{
	switch (level) {
		case eDEBUG:
			DoTraceLevel("DEBUG: ", logMsg);
			break;
		case eINFO:
			DoTraceLevel("INFO: ", logMsg);
			break;
		case eWARNING:
			DoTraceLevel("WARNING: ", logMsg);
			break;
		case eERR:
			DoTraceLevel("ERROR: ", logMsg);
			break;
		case eALERT:
			DoTraceLevel("ALERT: ", logMsg);
			break;

		default:
			break;
	}
}

//---- UnixSysLog ----------------------------------------------------------------
#if defined(__sun) || defined(__linux__) || defined(__aix__)

UnixSysLog::UnixSysLog(const char *appId)
{
	// initialize the syslog channel
	::openlog(appId, LOG_PID, LOG_USER);
}

UnixSysLog::~UnixSysLog()
{
	// close the syslog channel for this application
	::closelog();
}

void UnixSysLog::DoSystemLevelLog(eLogLevel level, const char *logMsg)
{
	// need to do a remapping of internal code to unix specific code now
	long lLevel = LOG_DEBUG;
	switch (level) {
		case eINFO:
			lLevel = LOG_INFO;
			break;
		case eWARNING:
			lLevel = LOG_WARNING;
			break;
		case eERR:
			lLevel = LOG_ERR;
			break;
		case eALERT:
			lLevel = LOG_ALERT;
			break;
		default:
			break;
	};
	::syslog(lLevel, "%s", (const char *)logMsg);
}

#endif

#if defined(__370__)

void S370SysLog::DoSystemLevelLog(eLogLevel level, const char *msg)
{
	cerr << "level " << level << ": " << logMsg << endl;
}

void SysLog::DoLogTrace(long level, const char *msg)
{
	// do nothing we already logged it on cerr
}

#endif

#if defined(WIN32)
Win32SysLog::Win32SysLog(const char *appId)
{
	fLogHandle =	::RegisterEventSource(0, appId);
}

Win32SysLog::~Win32SysLog()
{
	::DeregisterEventSource( fLogHandle );
}

void Win32SysLog::DoSystemLevelLog(eLogLevel level, const char *logMsg)
{
	WORD evtType = EVENTLOG_INFORMATION_TYPE;
	LPCTSTR str[1];
	str[0] = logMsg;

	switch (level) {
		case eWARNING:
			evtType = EVENTLOG_WARNING_TYPE;
			break;
		case eERR:
		case eALERT:
			evtType = EVENTLOG_ERROR_TYPE;
			break;
		default:
			break;
	}
	::ReportEvent(fLogHandle, evtType, 0, 0, 0, 2, 0, str, 0);
}
#endif
<commit_msg>resolved mac compatibility issue<commit_after>/*
 * Copyright (c) 2005, Peter Sommerlad and IFS Institute for Software at HSR Rapperswil, Switzerland
 * All rights reserved.
 *
 * This library/application is free software; you can redistribute and/or modify it under the terms of
 * the license that is included with this library/application in the file license.txt.
 */

//--- interface include --------------------------------------------------------
#include "SysLog.h"
#if defined(WIN32)
#define IOSTREAM_IS_THREADSAFE
#endif

//--- standard modules used ----------------------------------------------------
#include "System.h"

//--- c-library modules used ---------------------------------------------------
#include <errno.h>
#include <cstdio>
#include <cstring>
#if !defined(WIN32)
#include <syslog.h>
#endif
#if defined(__SUNPRO_CC)
#include <strings.h>
#include <stdio.h>
#endif

//--- SysLog ----------------------------------------------------------
SysLog *SysLog::fgSysLog = 0;
SysLog::eLogLevel SysLog::fgDoSystemLevelLog = SysLog::eALERT;
SysLog::eLogLevel SysLog::fgDoLogOnCerr = SysLog::eERR;

//--- module initialization/termination
void SysLog::Init(const char *appId)
{
	// beware this is not thread safe
	// the syslog channel is initialized
	// by the main thread in the server constructor
	// but since logging takes place before the
	// server object exists we have to take care

	if ( appId ) {
		Terminate();    // close existing syslog channel with default Coast name
	} else {
		appId = "Coast";
	}

	String strValue = System::EnvGet("WD_DOLOG");
	int iValue = (int)strValue.AsLong(eNone);
	if ( ( eNone < iValue ) && ( iValue < eLast ) ) {
		fgDoSystemLevelLog = (eLogLevel)iValue;
	}
	strValue = System::EnvGet("WD_LOGONCERR");
	iValue = (int)strValue.AsLong(eNone);
	if ( ( eNone < iValue ) && ( iValue < eLast ) ) {
		fgDoLogOnCerr = (eLogLevel)iValue;
	}

	// initialize the syslog channel
	if ( !fgSysLog ) {
		// open the syslog channel for this application
		// there is always only one syslog channel per application
#if defined(WIN32)
		fgSysLog = new Win32SysLog(appId);
#elif defined(__370__)
		fgSysLog = new S370SysLog();
#else
		fgSysLog = new UnixSysLog(appId);
#endif
	}
}

void SysLog::Terminate()
{
	if ( fgSysLog ) {
		delete fgSysLog;
		fgSysLog = 0;
	}
}

//--- logging API -------------------------------------

// severity eDEBUG for tracing server
// activity during development and
// deployment
void SysLog::Debug(const char *msg)
{
	Log(eDEBUG, msg);
}

// severity eINFO for general information
// log general useful information about server
// activity
void SysLog::Info(const char *msg)
{
	Log(eINFO, msg);
}

// severity eWARNING	for information about
// inconsistent state or potential dangerous
// situation
void SysLog::Warning(const char *msg)
{
	Log(eWARNING, msg);
}

// severity eERR for outright errors
// during operation of the server without
// fatal results
void SysLog::Error(const char *msg)
{
	Log(eERR, msg);
}

// severity eALERT for fatal errors
// this call triggers also an alert on
// the operator console
void SysLog::Alert(const char *msg)
{
	Log(eALERT, msg);
}

// bottleneck routine used by others
// here you can use severity levels directly
void SysLog::Log(eLogLevel level, const char *msg)
{
	if ( InitOnce() ) {
		fgSysLog->DoLog(level, msg);
	}
}

bool SysLog::InitOnce()
{
	static bool once = false;
	if (!once) {
		once = true;
		Init(0);
	}
	return (fgSysLog != NULL);
}

//--- utilities ------------------------------------

// const char *SysErrorMsg(long errnum) maps the error number in errnum
// to an error  message  string,  and returns a pointer to that string.
// SysErrorMsg(long errnum) uses the same set of error messages as perror().
// The returned string should not be overwritten.
String SysLog::SysErrorMsg(long errnum)
{
#if defined(WIN32)
	LPVOID lpMsgBuf;

	FormatMessage(
		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
		NULL,
		errnum,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
		(LPTSTR) &lpMsgBuf,
		0,
		NULL
	);

	String msg((const char *)lpMsgBuf, strlen(lpMsgBuf), Storage::Global());
	// Free the buffer.
	LocalFree( lpMsgBuf );
	return msg;
#else
	String theError(Storage::Global());
	if (errnum == EMFILE) {
		theError = "[No more filehandles in Process.] ";
	}
	if (errnum == ENFILE) {
		theError = "[No more filehandles in System.] ";
	}
	theError.Append((long)errnum).Append(' ').Append(strerror(errnum));
	return theError;
#endif
}

// const char *LastSysError() returns the error message retrieved with errno
// from SysErrorMsg(errno)
String SysLog::LastSysError()
{
	int iError( System::GetSystemError() );
	if ( iError != 0 ) {
		return SysErrorMsg( iError );
	}
	return String().Append((long)iError).Append(": no system-error");
}

//magic function to be used by Assert macro to avoid dependency to class SysLog and SysLog.h
int syslog_assert(const char *file, long line, const char *assertion)
{
	return SysLog::LogAssert(file, line, assertion);
}

int SysLog::LogAssert(const char *file, long line, const char *assertion)
{
	// implement brain dead with C style things to avoid problems of recursion
	char asrt_buf[2048];
	int asrt_buf_used = sprintf(asrt_buf, "%s:%ld\n Assert(%s) failed\n", file, line, assertion);
	\
	SysLog::WriteToStderr(asrt_buf, asrt_buf_used);
	return 0;
}

void SysLog::WriteToStderr(const String &msg)
{
	SysLog::WriteToStderr((const char *)msg, msg.Length());
}

void SysLog::WriteToStderr(char *msg, long length)
{
	SysLog::WriteToStderr((const char *)msg, length);
}

void SysLog::WriteToStderr(const char *msg, long length)
{
	if ( msg ) {
		long sLen = length;
		if ( sLen < 0 ) {
			sLen = strlen(msg);
		}
#ifdef IOSTREAM_IS_THREADSAFE
		cerr.write(msg, sLen).flush();
#else
		::write(2, msg, sLen);
#endif
	}
}

void SysLog::WriteToStdout(const String &msg)
{
	SysLog::WriteToStdout((const char *)msg, msg.Length());
}

void SysLog::WriteToStdout(char *msg, long length)
{
	SysLog::WriteToStdout((const char *)msg, length);
}

void SysLog::WriteToStdout(const char *msg, long length)
{
	if ( msg ) {
		long sLen = length;
		if ( sLen < 0 ) {
			sLen = strlen(msg);
		}
#ifdef IOSTREAM_IS_THREADSAFE
		cout.write(msg, sLen).flush();
#else
		::write(1, msg, sLen);
#endif
	}
}

SysLog::SysLog()
{
}

SysLog::~SysLog()
{
}

void SysLog::DoLog(eLogLevel level, const char *msg)
{
	// override logging parameter if it is an alert message
	if ( level >= fgDoSystemLevelLog ) {
		DoSystemLevelLog(level, msg);
	}
	// override logging parameter if it is an error or alert message
	if ( level >= fgDoLogOnCerr ) {
		DoLogTrace(level, msg);
	}
}

void SysLog::DoTraceLevel(const char *level, const char *msg)
{
	String finalMessage(level, strlen(level), Storage::Global());
	finalMessage.Append(msg);
	finalMessage.Append('\n');
	SysLog::WriteToStderr(finalMessage, finalMessage.Length());
}

void SysLog::DoLogTrace(eLogLevel level, const char *logMsg)
{
	switch (level) {
		case eDEBUG:
			DoTraceLevel("DEBUG: ", logMsg);
			break;
		case eINFO:
			DoTraceLevel("INFO: ", logMsg);
			break;
		case eWARNING:
			DoTraceLevel("WARNING: ", logMsg);
			break;
		case eERR:
			DoTraceLevel("ERROR: ", logMsg);
			break;
		case eALERT:
			DoTraceLevel("ALERT: ", logMsg);
			break;

		default:
			break;
	}
}

//---- UnixSysLog ----------------------------------------------------------------
#if defined(WIN32)
Win32SysLog::Win32SysLog(const char *appId)
{
	fLogHandle =	::RegisterEventSource(0, appId);
}

Win32SysLog::~Win32SysLog()
{
	::DeregisterEventSource( fLogHandle );
}

void Win32SysLog::DoSystemLevelLog(eLogLevel level, const char *logMsg)
{
	WORD evtType = EVENTLOG_INFORMATION_TYPE;
	LPCTSTR str[1];
	str[0] = logMsg;

	switch (level) {
		case eWARNING:
			evtType = EVENTLOG_WARNING_TYPE;
			break;
		case eERR:
		case eALERT:
			evtType = EVENTLOG_ERROR_TYPE;
			break;
		default:
			break;
	}
	::ReportEvent(fLogHandle, evtType, 0, 0, 0, 2, 0, str, 0);
}
#elif defined(__370__)
void S370SysLog::DoSystemLevelLog(eLogLevel level, const char *msg)
{
	cerr << "level " << level << ": " << logMsg << endl;
}

void SysLog::DoLogTrace(long level, const char *msg)
{
	// do nothing we already logged it on cerr
}
#else
UnixSysLog::UnixSysLog(const char *appId)
{
	// initialize the syslog channel
	::openlog(appId, LOG_PID, LOG_USER);
}

UnixSysLog::~UnixSysLog()
{
	// close the syslog channel for this application
	::closelog();
}

void UnixSysLog::DoSystemLevelLog(eLogLevel level, const char *logMsg)
{
	// need to do a remapping of internal code to unix specific code now
	long lLevel = LOG_DEBUG;
	switch (level) {
		case eINFO:
			lLevel = LOG_INFO;
			break;
		case eWARNING:
			lLevel = LOG_WARNING;
			break;
		case eERR:
			lLevel = LOG_ERR;
			break;
		case eALERT:
			lLevel = LOG_ALERT;
			break;
		default:
			break;
	};
	::syslog(lLevel, "%s", (const char *)logMsg);
}
#endif

<|endoftext|>


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

  Program:   Visualization Toolkit
  Module:    vtkChartLegend.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

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

#include "vtkChartLegend.h"

#include "vtkContext2D.h"
#include "vtkPen.h"
#include "vtkBrush.h"
#include "vtkChart.h"
#include "vtkPlot.h"
#include "vtkTextProperty.h"
#include "vtkStdString.h"
#include "vtkVector.h"
#include "vtkWeakPointer.h"
#include "vtkSmartPointer.h"
#include "vtkStringArray.h"

#include "vtkObjectFactory.h"

#include <vector>

//-----------------------------------------------------------------------------
class vtkChartLegend::Private
{
public:
  Private()
  {
  }
  ~Private()
  {
  }

  vtkVector2f Point;
  vtkWeakPointer<vtkChart> Chart;
  std::vector<vtkPlot*> ActivePlots;
};

//-----------------------------------------------------------------------------
vtkStandardNewMacro(vtkChartLegend);

//-----------------------------------------------------------------------------
vtkChartLegend::vtkChartLegend()
{
  this->Storage = new vtkChartLegend::Private;
  this->Point = this->Storage->Point.GetData();
  // Defaults to 12pt text, with top, right alignment to the specified point.
  this->LabelProperties = vtkSmartPointer<vtkTextProperty>::New();
  this->LabelProperties->SetFontSize(12);
  this->LabelProperties->SetColor(0.0, 0.0, 0.0);
  this->LabelProperties->SetJustificationToLeft();
  this->LabelProperties->SetVerticalJustificationToBottom();

  this->Pen = vtkSmartPointer<vtkPen>::New();
  this->Pen->SetColor(0, 0, 0);
  this->Brush = vtkSmartPointer<vtkBrush>::New();
  this->Brush->SetColor(255, 255, 255, 255);
  this->HorizontalAlignment = vtkChartLegend::RIGHT;
  this->VerticalAlignment = vtkChartLegend::TOP;

  this->Padding = 5;
  this->SymbolWidth = 25;
  this->Inline = true;
}

//-----------------------------------------------------------------------------
vtkChartLegend::~vtkChartLegend()
{
  delete this->Storage;
  this->Storage = NULL;
  this->Point = NULL;
}

//-----------------------------------------------------------------------------
void vtkChartLegend::Update()
{
  this->Storage->ActivePlots.clear();
  for (int i = 0; i < this->Storage->Chart->GetNumberOfPlots(); ++i)
    {
    if (this->Storage->Chart->GetPlot(i)->GetVisible())
      {
      this->Storage->ActivePlots.push_back(this->Storage->Chart->GetPlot(i));
      }
    // If we have a plot with multiple labels, we generally only want to show
    // the labels/legend symbols for the first one. So truncate at the first
    // one we encounter.
    if (this->Storage->Chart->GetPlot(i)->GetLabels() &&
        this->Storage->Chart->GetPlot(i)->GetLabels()->GetNumberOfTuples() > 1)
      {
      break;
      }
    }
  this->PlotTime.Modified();
}

//-----------------------------------------------------------------------------
bool vtkChartLegend::Paint(vtkContext2D *painter)
{
  // This is where everything should be drawn, or dispatched to other methods.
  vtkDebugMacro(<< "Paint event called in vtkChartLegend.");

  if (!this->Visible)
    {
    return true;
    }

  this->GetBoundingRect(painter);

  // Now draw a box for the legend.
  painter->ApplyPen(this->Pen);
  painter->ApplyBrush(this->Brush);
  painter->DrawRect(this->Rect.X(), this->Rect.Y(),
                    this->Rect.Width(), this->Rect.Height());

  painter->ApplyTextProp(this->LabelProperties);

  vtkVector2f stringBounds[2];
  painter->ComputeStringBounds("Tgyf", stringBounds->GetData());
  float height = stringBounds[1].GetY();
  painter->ComputeStringBounds("The", stringBounds->GetData());
  float baseHeight = stringBounds[1].GetY();

  vtkVector2f pos(this->Rect.X() + this->Padding + this->SymbolWidth,
                  this->Rect.Y() + this->Rect.Height() - this->Padding - floor(height));
  vtkRectf rect(this->Rect.X() + this->Padding, pos.Y(),
               this->SymbolWidth-3, ceil(height));

  // Draw all of the legend labels and marks
  for(size_t i = 0; i < this->Storage->ActivePlots.size(); ++i)
    {
    vtkStringArray *labels = this->Storage->ActivePlots[i]->GetLabels();
    for (vtkIdType l = 0; labels && (l < labels->GetNumberOfValues()); ++l)
      {
      // This is fairly hackish, but gets the text looking reasonable...
      // Calculate a height for a "normal" string, then if this height is greater
      // that offset is used to move it down. Effectively hacking in a text
      // base line until better support is in the text rendering code...
      // There are still several one pixel glitches, but it looks better than
      // using the default vertical alignment. FIXME!
      vtkStdString testString = labels->GetValue(l);
      testString += "T";
      painter->ComputeStringBounds(testString, stringBounds->GetData());
      painter->DrawString(pos.X(), rect.Y() + (baseHeight-stringBounds[1].Y()),
                          labels->GetValue(l));

      // Paint the legend mark and increment out y value.
      this->Storage->ActivePlots[i]->PaintLegend(painter, rect, l);
      rect.SetY(rect.Y() - height - this->Padding);
      }
    }

  return true;
}

//-----------------------------------------------------------------------------
vtkRectf vtkChartLegend::GetBoundingRect(vtkContext2D *painter)
{
  if (this->RectTime > this->GetMTime() && this->RectTime > this->PlotTime)
    {
    return this->Rect;
    }

  painter->ApplyTextProp(this->LabelProperties);

  vtkVector2f stringBounds[2];
  painter->ComputeStringBounds("Tgyf", stringBounds->GetData());
  float height = stringBounds[1].GetY();
  float maxWidth = 0.0f;

  // Calculate the widest legend label - needs the context to calculate font
  // metrics, but these could be cached.
  for(size_t i = 0; i < this->Storage->ActivePlots.size(); ++i)
    {
    vtkStringArray *labels = this->Storage->ActivePlots[i]->GetLabels();
    for (vtkIdType l = 0; labels && (l < labels->GetNumberOfTuples()); ++l)
      {
      painter->ComputeStringBounds(labels->GetValue(l),
                                   stringBounds->GetData());
      if (stringBounds[1].X() > maxWidth)
        {
        maxWidth = stringBounds[1].X();
        }
      }
    }

  // Figure out the size of the legend box and store locally.
  int numLabels = 0;
  for(size_t i = 0; i < this->Storage->ActivePlots.size(); ++i)
    {
    numLabels += this->Storage->ActivePlots[i]->GetNumberOfLabels();
    }

  // Default point placement is bottom left.
  this->Rect = vtkRectf(floor(this->Storage->Point.X()),
                        floor(this->Storage->Point.Y()),
                        ceil(maxWidth + 2 * this->Padding + this->SymbolWidth),
                        ceil((numLabels * (height + this->Padding)) + this->Padding));

  this->RectTime.Modified();
  return this->Rect;
}

//-----------------------------------------------------------------------------
void vtkChartLegend::SetPoint(const vtkVector2f &point)
{
  this->Storage->Point = point;
  this->Modified();
}

//-----------------------------------------------------------------------------
const vtkVector2f& vtkChartLegend::GetPointVector()
{
  return this->Storage->Point;
}

//-----------------------------------------------------------------------------
void vtkChartLegend::SetLabelSize(int size)
{
  this->LabelProperties->SetFontSize(size);
}

//-----------------------------------------------------------------------------
int vtkChartLegend::GetLabelSize()
{
  return this->LabelProperties->GetFontSize();
}

//-----------------------------------------------------------------------------
vtkTextProperty * vtkChartLegend::GetLabelProperties()
{
  return this->LabelProperties;
}

//-----------------------------------------------------------------------------
void vtkChartLegend::SetChart(vtkChart* chart)
{
  if (this->Storage->Chart == chart)
    {
    return;
    }
  else
    {
    this->Storage->Chart = chart;
    this->Modified();
    }
}

//-----------------------------------------------------------------------------
vtkChart* vtkChartLegend::GetChart()
{
  return this->Storage->Chart;
}

//-----------------------------------------------------------------------------
void vtkChartLegend::PrintSelf(ostream &os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
}
<commit_msg>BUG: Only render legend items that have a label.<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkChartLegend.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

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

#include "vtkChartLegend.h"

#include "vtkContext2D.h"
#include "vtkPen.h"
#include "vtkBrush.h"
#include "vtkChart.h"
#include "vtkPlot.h"
#include "vtkTextProperty.h"
#include "vtkStdString.h"
#include "vtkVector.h"
#include "vtkWeakPointer.h"
#include "vtkSmartPointer.h"
#include "vtkStringArray.h"

#include "vtkObjectFactory.h"

#include <vector>

//-----------------------------------------------------------------------------
class vtkChartLegend::Private
{
public:
  Private()
  {
  }
  ~Private()
  {
  }

  vtkVector2f Point;
  vtkWeakPointer<vtkChart> Chart;
  std::vector<vtkPlot*> ActivePlots;
};

//-----------------------------------------------------------------------------
vtkStandardNewMacro(vtkChartLegend);

//-----------------------------------------------------------------------------
vtkChartLegend::vtkChartLegend()
{
  this->Storage = new vtkChartLegend::Private;
  this->Point = this->Storage->Point.GetData();
  // Defaults to 12pt text, with top, right alignment to the specified point.
  this->LabelProperties = vtkSmartPointer<vtkTextProperty>::New();
  this->LabelProperties->SetFontSize(12);
  this->LabelProperties->SetColor(0.0, 0.0, 0.0);
  this->LabelProperties->SetJustificationToLeft();
  this->LabelProperties->SetVerticalJustificationToBottom();

  this->Pen = vtkSmartPointer<vtkPen>::New();
  this->Pen->SetColor(0, 0, 0);
  this->Brush = vtkSmartPointer<vtkBrush>::New();
  this->Brush->SetColor(255, 255, 255, 255);
  this->HorizontalAlignment = vtkChartLegend::RIGHT;
  this->VerticalAlignment = vtkChartLegend::TOP;

  this->Padding = 5;
  this->SymbolWidth = 25;
  this->Inline = true;
}

//-----------------------------------------------------------------------------
vtkChartLegend::~vtkChartLegend()
{
  delete this->Storage;
  this->Storage = NULL;
  this->Point = NULL;
}

//-----------------------------------------------------------------------------
void vtkChartLegend::Update()
{
  this->Storage->ActivePlots.clear();
  for (int i = 0; i < this->Storage->Chart->GetNumberOfPlots(); ++i)
    {
    if (this->Storage->Chart->GetPlot(i)->GetVisible()
        && this->Storage->Chart->GetPlot(i)->GetLabel().length() > 0)
      {
      this->Storage->ActivePlots.push_back(this->Storage->Chart->GetPlot(i));
      }
    // If we have a plot with multiple labels, we generally only want to show
    // the labels/legend symbols for the first one. So truncate at the first
    // one we encounter.
    if (this->Storage->Chart->GetPlot(i)->GetLabels() &&
        this->Storage->Chart->GetPlot(i)->GetLabels()->GetNumberOfTuples() > 1)
      {
      break;
      }
    }
  this->PlotTime.Modified();
}

//-----------------------------------------------------------------------------
bool vtkChartLegend::Paint(vtkContext2D *painter)
{
  // This is where everything should be drawn, or dispatched to other methods.
  vtkDebugMacro(<< "Paint event called in vtkChartLegend.");

  if (!this->Visible)
    {
    return true;
    }

  this->GetBoundingRect(painter);

  // Now draw a box for the legend.
  painter->ApplyPen(this->Pen);
  painter->ApplyBrush(this->Brush);
  painter->DrawRect(this->Rect.X(), this->Rect.Y(),
                    this->Rect.Width(), this->Rect.Height());

  painter->ApplyTextProp(this->LabelProperties);

  vtkVector2f stringBounds[2];
  painter->ComputeStringBounds("Tgyf", stringBounds->GetData());
  float height = stringBounds[1].GetY();
  painter->ComputeStringBounds("The", stringBounds->GetData());
  float baseHeight = stringBounds[1].GetY();

  vtkVector2f pos(this->Rect.X() + this->Padding + this->SymbolWidth,
                  this->Rect.Y() + this->Rect.Height() - this->Padding - floor(height));
  vtkRectf rect(this->Rect.X() + this->Padding, pos.Y(),
               this->SymbolWidth-3, ceil(height));

  // Draw all of the legend labels and marks
  for(size_t i = 0; i < this->Storage->ActivePlots.size(); ++i)
    {
    vtkStringArray *labels = this->Storage->ActivePlots[i]->GetLabels();
    for (vtkIdType l = 0; labels && (l < labels->GetNumberOfValues()); ++l)
      {
      // This is fairly hackish, but gets the text looking reasonable...
      // Calculate a height for a "normal" string, then if this height is greater
      // that offset is used to move it down. Effectively hacking in a text
      // base line until better support is in the text rendering code...
      // There are still several one pixel glitches, but it looks better than
      // using the default vertical alignment. FIXME!
      vtkStdString testString = labels->GetValue(l);
      testString += "T";
      painter->ComputeStringBounds(testString, stringBounds->GetData());
      painter->DrawString(pos.X(), rect.Y() + (baseHeight-stringBounds[1].Y()),
                          labels->GetValue(l));

      // Paint the legend mark and increment out y value.
      this->Storage->ActivePlots[i]->PaintLegend(painter, rect, l);
      rect.SetY(rect.Y() - height - this->Padding);
      }
    }

  return true;
}

//-----------------------------------------------------------------------------
vtkRectf vtkChartLegend::GetBoundingRect(vtkContext2D *painter)
{
  if (this->RectTime > this->GetMTime() && this->RectTime > this->PlotTime)
    {
    return this->Rect;
    }

  painter->ApplyTextProp(this->LabelProperties);

  vtkVector2f stringBounds[2];
  painter->ComputeStringBounds("Tgyf", stringBounds->GetData());
  float height = stringBounds[1].GetY();
  float maxWidth = 0.0f;

  // Calculate the widest legend label - needs the context to calculate font
  // metrics, but these could be cached.
  for(size_t i = 0; i < this->Storage->ActivePlots.size(); ++i)
    {
    vtkStringArray *labels = this->Storage->ActivePlots[i]->GetLabels();
    for (vtkIdType l = 0; labels && (l < labels->GetNumberOfTuples()); ++l)
      {
      painter->ComputeStringBounds(labels->GetValue(l),
                                   stringBounds->GetData());
      if (stringBounds[1].X() > maxWidth)
        {
        maxWidth = stringBounds[1].X();
        }
      }
    }

  // Figure out the size of the legend box and store locally.
  int numLabels = 0;
  for(size_t i = 0; i < this->Storage->ActivePlots.size(); ++i)
    {
    numLabels += this->Storage->ActivePlots[i]->GetNumberOfLabels();
    }

  // Default point placement is bottom left.
  this->Rect = vtkRectf(floor(this->Storage->Point.X()),
                        floor(this->Storage->Point.Y()),
                        ceil(maxWidth + 2 * this->Padding + this->SymbolWidth),
                        ceil((numLabels * (height + this->Padding)) + this->Padding));

  this->RectTime.Modified();
  return this->Rect;
}

//-----------------------------------------------------------------------------
void vtkChartLegend::SetPoint(const vtkVector2f &point)
{
  this->Storage->Point = point;
  this->Modified();
}

//-----------------------------------------------------------------------------
const vtkVector2f& vtkChartLegend::GetPointVector()
{
  return this->Storage->Point;
}

//-----------------------------------------------------------------------------
void vtkChartLegend::SetLabelSize(int size)
{
  this->LabelProperties->SetFontSize(size);
}

//-----------------------------------------------------------------------------
int vtkChartLegend::GetLabelSize()
{
  return this->LabelProperties->GetFontSize();
}

//-----------------------------------------------------------------------------
vtkTextProperty * vtkChartLegend::GetLabelProperties()
{
  return this->LabelProperties;
}

//-----------------------------------------------------------------------------
void vtkChartLegend::SetChart(vtkChart* chart)
{
  if (this->Storage->Chart == chart)
    {
    return;
    }
  else
    {
    this->Storage->Chart = chart;
    this->Modified();
    }
}

//-----------------------------------------------------------------------------
vtkChart* vtkChartLegend::GetChart()
{
  return this->Storage->Chart;
}

//-----------------------------------------------------------------------------
void vtkChartLegend::PrintSelf(ostream &os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
}
<|endoftext|>


<commit_before>#include "MainMenuLayer.h"
#include "cocostudio/CocoStudio.h"

USING_NS_CC;

int MainMenuLayer::s_iCurrentGold = 0;

MainMenuLayer::MainMenuLayer()
{
}

MainMenuLayer::~MainMenuLayer()
{
}

bool MainMenuLayer::init()
{
	if (!Layer::init())
	{
		return false;
	}
	auto widget = cocostudio::GUIReader::getInstance()->widgetFromJsonFile("HUD/HUD.json");
	pGoldText = (ui::TextAtlas *)widget->getChildByName("gold");
	pGoldText->setStringValue("0");
	this->addChild(widget,100);

	Size visibleSize = Director::getInstance()->getVisibleSize();
	Vec2 origin = Director::getInstance()->getVisibleOrigin();

	auto closeItem = MenuItemImage::create(
		"CloseNormal.png",
		"CloseSelected.png",
		CC_CALLBACK_1(MainMenuLayer::menuCloseCallback, this));

	closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width / 2,
		origin.y + closeItem->getContentSize().height / 2));

	auto menu = Menu::create(closeItem, NULL);
	menu->setPosition(Vec2::ZERO);
	this->addChild(menu, 100);

	auto label = Label::createWithTTF("Game Parkour", "fonts/Marker Felt.ttf", 24);
	label->setPosition(Vec2(origin.x + visibleSize.width / 2,
		origin.y + visibleSize.height - label->getContentSize().height));

	this->addChild(label,100);

	//this->scheduleUpdate();

	return true;
}

void MainMenuLayer::menuCloseCallback(Ref* pSender)
{
	Director::getInstance()->end();

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
	exit(0);
#endif
}

void MainMenuLayer::update(float delta)
{

}<commit_msg>finished pause button func<commit_after>#include "MainMenuLayer.h"
#include "cocostudio/CocoStudio.h"
#include "ui/UIButton.h"

USING_NS_CC;

int MainMenuLayer::s_iCurrentGold = 0;

MainMenuLayer::MainMenuLayer()
{
}

MainMenuLayer::~MainMenuLayer()
{
}

bool MainMenuLayer::init()
{
	if (!Layer::init())
	{
		return false;
	}
	auto widget = cocostudio::GUIReader::getInstance()->widgetFromJsonFile("HUD/HUD.json");
	pGoldText = (ui::TextAtlas *)widget->getChildByName("gold");
	pGoldText->setString("0");
	this->addChild(widget,100);

	auto pauseButton = static_cast<cocos2d::ui::Button *>(widget->getChildByName("Button_1"));
	pauseButton->addTouchEventListener(
		[&](Ref * sender,cocos2d::ui::Widget::TouchEventType type)
		{
			static bool bIsPaused = false;
			if (type == cocos2d::ui::Widget::TouchEventType::ENDED)
			{
				if (bIsPaused)
				{
					bIsPaused = false;
					Director::getInstance()->resume();
				}
				else
				{
					Director::getInstance()->pause();
					bIsPaused = true;
				}
			}//if_type

		}//lambdy
	);

	Size visibleSize = Director::getInstance()->getVisibleSize();
	Vec2 origin = Director::getInstance()->getVisibleOrigin();

	auto closeItem = MenuItemImage::create(
		"CloseNormal.png",
		"CloseSelected.png",
		CC_CALLBACK_1(MainMenuLayer::menuCloseCallback, this));

	closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width / 2,
		origin.y + closeItem->getContentSize().height / 2));

	auto menu = Menu::create(closeItem, NULL);
	menu->setPosition(Vec2::ZERO);
	this->addChild(menu, 100);

	auto label = Label::createWithTTF("Game Parkour", "fonts/Marker Felt.ttf", 24);
	label->setPosition(Vec2(origin.x + visibleSize.width / 2,
		origin.y + visibleSize.height - label->getContentSize().height));

	this->addChild(label,100);

	//this->scheduleUpdate();

	return true;
}

void MainMenuLayer::menuCloseCallback(Ref* pSender)
{
	Director::getInstance()->end();

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
	exit(0);
#endif
}

void MainMenuLayer::update(float delta)
{

}<|endoftext|>


<commit_before>/*
 *  cpptest       libcaca++ rendering test
 *  Copyright (c) 2006 Jean-Yves Lamoureux <jylam@lnxscene.org>
 *                All Rights Reserved
 *
 *  $Id$
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the Do What The Fuck You Want To
 *  Public License, Version 2, as published by Sam Hocevar. See
 *  http://sam.zoy.org/wtfpl/COPYING for more details.
 */

#include "config.h"

#include <iostream>

#include <cucul++.h>
#include <caca++.h>

using namespace std;

static char const *pig[]= {
    "                                   ",
    "                             _     ",
    "    _._ _..._ .-',     _.._(`))    ",
    "   '-. `     '  /-._.-'    ',/     ",
    "      )         \\            '.    ",
    "     / _    _    |             \\   ",
    "    |  a    a    /              |  ",
    "    \\   .-.                     ;  " ,
    "     '-('' ).-'       ,'       ;   ",
    "        '-;           |      .'    ",
    "           \\           \\    /      ",
    "           | 7  .__  _.-\\   \\      ",
    "           | |  |  ``/  /`  /      ",
    "      jgs /,_|  |   /,_/   /       ",
    "             /,_/      '`-'        ",
    "                                   ",
 NULL
};

int main(int argc, char *argv[])
{
    Cucul *qq;
    Caca  *kk;
    Event ev;

    int x = 0, y = 0, ix = 1, iy = 1;

    try {
        qq = new Cucul();
    }
    catch (int e) {
        cerr << "Error while initializing cucul (" << e << ")" << endl;
        return -1;
    }

    try {
        kk = new Caca(qq);
    }
    catch(int e) {
        cerr << "Error while attaching cucul to caca (" << e << ")" << endl;
        return -1;
    }

    kk->setDisplayTime(20000);

    while(!kk->getEvent(ev.CACA_EVENT_KEY_PRESS, &ev, 0)) {

        /* Draw pig */
        qq->setColorANSI(CUCUL_LIGHTMAGENTA, CUCUL_BLACK);

        for(int i = 0; pig[i]; i++)
            qq->putStr(x, y+i, (char*)pig[i]);

        /* printf works */
        qq->setColorANSI(CUCUL_LIGHTBLUE, CUCUL_BLACK);
        qq->Printf((qq->getWidth()/2) - 10 ,qq->getHeight()/2, "Powered by libcaca %s", VERSION);

        /* Blit */
        kk->Display();
        qq->Clear();

        x+=ix;
        y+=iy;

        if(x>=(qq->getWidth()-35)  || x<0 )
            ix=-ix;
        if(y>=(qq->getHeight()-15)   || y<0 )
            iy=-iy;

    }

    delete kk;
    delete qq;

    return 0;
}
<commit_msg>  * Use the import function to load the pig image in cxxtest.<commit_after>/*
 *  cpptest       libcaca++ rendering test
 *  Copyright (c) 2006 Jean-Yves Lamoureux <jylam@lnxscene.org>
 *                All Rights Reserved
 *
 *  $Id$
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the Do What The Fuck You Want To
 *  Public License, Version 2, as published by Sam Hocevar. See
 *  http://sam.zoy.org/wtfpl/COPYING for more details.
 */

#include "config.h"

#include <iostream>

#include <cucul++.h>
#include <caca++.h>

using namespace std;

static char const pigstring[] =
    "                                   \n"
    "                             _     \n"
    "    _._ _..._ .-',     _.._(`))    \n"
    "   '-. `     '  /-._.-'    ',/     \n"
    "      )         \\            '.    \n"
    "     / _    _    |             \\   \n"
    "    |  a    a    /              |  \n"
    "    \\   .-.                     ;  \n"
    "     '-('' ).-'       ,'       ;   \n"
    "        '-;           |      .'    \n"
    "           \\           \\    /      \n"
    "           | 7  .__  _.-\\   \\      \n"
    "           | |  |  ``/  /`  /      \n"
    "      jgs /,_|  |   /,_/   /       \n"
    "             /,_/      '`-'        \n";

int main(int argc, char *argv[])
{
    Cucul *qq, *pig;
    Caca  *kk;
    Event ev;

    int x = 0, y = 0, ix = 1, iy = 1;

    try {
        qq = new Cucul();
    }
    catch (int e) {
        cerr << "Error while initializing cucul (" << e << ")" << endl;
        return -1;
    }

    try {
        kk = new Caca(qq);
    }
    catch(int e) {
        cerr << "Error while attaching cucul to caca (" << e << ")" << endl;
        return -1;
    }

    try {
        // Import buffer into a canvas
        Buffer *buf = new Buffer();
        buf->loadMemory((void *)pigstring, strlen(pigstring));
        pig = new Cucul(buf, "text");
        delete buf;
        // Change colour to magenta
        pig->setColorANSI(CUCUL_LIGHTMAGENTA, CUCUL_TRANSPARENT);
        for(int y = 0; y < pig->getHeight(); y++)
            for(int x = 0; x < pig->getWidth(); x++)
                pig->putChar(x, y, pig->getChar(x, y));
    }
    catch(int e) {
        cerr << "Error while importing image (" << e << ")" << endl;
        return -1;
    }

    kk->setDisplayTime(20000);

    while(!kk->getEvent(ev.CACA_EVENT_KEY_PRESS, &ev, 0)) {

        qq->Clear();

        /* Draw pig */
        qq->Blit(x, y, pig, NULL);

        /* printf works */
        qq->setColorANSI(CUCUL_LIGHTBLUE, CUCUL_BLACK);
        qq->Printf(qq->getWidth() / 2 - 10, qq->getHeight() / 2,
                   "Powered by libcaca %s", VERSION);

        /* Blit */
        kk->Display();

        x += ix;
        y += iy;

        if(x + pig->getWidth() >= qq->getWidth() || x < 0 )
            ix = -ix;
        if(y + pig->getHeight() >= qq->getHeight() || y < 0 )
            iy = -iy;
    }

    delete kk;
    delete qq;

    return 0;
}
<|endoftext|>


<commit_before>#include <jni.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <locale.h>

#include <mpv/client.h>
#include <mpv/opengl_cb.h>

#include <EGL/egl.h>

extern "C" {
    #include <libavcodec/jni.h>
}

#include "main.h"

#define ARRAYLEN(a) (sizeof(a)/sizeof(a[0]))
#define jni_func_name(name) Java_is_xyz_mpv_MPVLib_##name
#define jni_func(return_type, name, ...) JNIEXPORT return_type JNICALL jni_func_name(name) (JNIEnv *env, jobject obj, ##__VA_ARGS__)

extern "C" {
    jni_func(void, create);
    jni_func(void, init);
    jni_func(void, destroy);

    jni_func(void, initGL);
    jni_func(void, destroyGL);

    jni_func(void, command, jobjectArray jarray);

    jni_func(void, resize, jint width, jint height);
    jni_func(void, draw);
    jni_func(void, step);

    jni_func(jint, setOptionString, jstring option, jstring value);

    jni_func(jobject, getPropertyInt, jstring property);
    jni_func(void, setPropertyInt, jstring property, jobject value);
    jni_func(jobject, getPropertyBoolean, jstring property);
    jni_func(void, setPropertyBoolean, jstring property, jobject value);
    jni_func(jstring, getPropertyString, jstring jproperty);
    jni_func(void, setPropertyString, jstring jproperty, jstring jvalue);

    jni_func(void, observeProperty, jstring property, jint format);
};

mpv_handle *mpv;
mpv_opengl_cb_context *mpv_gl;
int g_width, g_height;

static void die(const char *msg)
{
    ALOGE("%s", msg);
    exit(1);
}

static void *get_proc_address_mpv(void *fn_ctx, const char *name)
{
    return (void*)eglGetProcAddress(name);
}

// Apparently it's considered slow to FindClass and GetMethodID every time we need them,
// so let's have a nice cache here
bool methods_initialized;
jclass java_Integer, java_Boolean;
jmethodID java_Integer_init, java_Integer_intValue, java_Boolean_init, java_Boolean_booleanValue;

static void init_methods_cache(JNIEnv *env) {
    if (methods_initialized)
        return;
    #define FIND_CLASS(name) reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(name)))
    java_Integer = FIND_CLASS("java/lang/Integer");
    java_Integer_init = env->GetMethodID(java_Integer, "<init>", "(I)V");
    java_Integer_intValue = env->GetMethodID(java_Integer, "intValue", "()I");
    java_Boolean = FIND_CLASS("java/lang/Boolean");
    java_Boolean_init = env->GetMethodID(java_Boolean, "<init>", "(Z)V");
    java_Boolean_booleanValue = env->GetMethodID(java_Boolean, "booleanValue", "()Z");
    #undef FIND_CLASS
    methods_initialized = true;
}

static void prepare_environment(JNIEnv *env) {
    setlocale(LC_NUMERIC, "C");

    JavaVM* vm = NULL;

    if (!env->GetJavaVM(&vm) && vm) {
        av_jni_set_java_vm(vm, NULL);
    }
    init_methods_cache(env);
}

jni_func(void, create) {
    prepare_environment(env);

    if (mpv)
        die("mpv is already initialized");

    mpv = mpv_create();
    if (!mpv)
        die("context init failed");
}

jni_func(void, init) {
    if (!mpv)
        die("mpv is not created");

    if (mpv_initialize(mpv) < 0)
        die("mpv init failed");

    mpv_request_log_messages(mpv, "v");
}

jni_func(void, destroy) {
    if (!mpv)
        die("mpv destroy called but it's already destroyed");
    mpv_terminate_destroy(mpv);
    mpv = NULL;
}

jni_func(void, initGL) {
    int ret = -1;
    if (!mpv)
        die("initGL: mpv not initialized");
    if (mpv_gl)
        die("OpenGL ES already initialized!?");

    mpv_gl = (mpv_opengl_cb_context*)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB);
    if (!mpv_gl)
        die("failed to create mpv GL API handle");

    if ((ret = mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address_mpv, NULL)) < 0) {
        ALOGE("mpv_opengl_cb_init_gl returned error %d", ret);
        die("failed to initialize mpv GL context");
    }
}

jni_func(void, destroyGL) {
    if (!mpv_gl)
        die("mpv_gl destroy called but it's already destroyed");
    mpv_opengl_cb_uninit_gl(mpv_gl);
    mpv_gl = NULL;
}

jni_func(void, command, jobjectArray jarray) {
    const char *arguments[128] = { 0 };
    int len = env->GetArrayLength(jarray);
    if (!mpv)
        die("Cannot run command: mpv is not initialized");
    if (len >= ARRAYLEN(arguments))
        die("Cannot run command: too many arguments");

    for (int i = 0; i < len; ++i)
        arguments[i] = env->GetStringUTFChars((jstring)env->GetObjectArrayElement(jarray, i), NULL);

    mpv_command(mpv, arguments);

    for (int i = 0; i < len; ++i)
        env->ReleaseStringUTFChars((jstring)env->GetObjectArrayElement(jarray, i), arguments[i]);
}

jni_func(void, resize, jint width, jint height) {
    ALOGV("Resizing! width: %d=>%d, %d=>%d\n", g_width, width, g_height, height);
    g_width = width;
    g_height = height;
}

jni_func(void, draw) {
    mpv_opengl_cb_draw(mpv_gl, 0, g_width, -g_height);
}

void sendPropertyUpdateToJava(JNIEnv *env, mpv_event_property *prop) {
    jmethodID mid;
    jstring jprop = env->NewStringUTF(prop->name);
    jclass clazz = env->FindClass("is/xyz/mpv/MPVLib");
    jstring jvalue = NULL;
    switch (prop->format) {
    case MPV_FORMAT_NONE:
        mid = env->GetStaticMethodID(clazz, "eventProperty", "(Ljava/lang/String;)V"); // eventProperty(String)
        env->CallStaticVoidMethod(clazz, mid, jprop);
        break;
    case MPV_FORMAT_FLAG:
        mid = env->GetStaticMethodID(clazz, "eventProperty", "(Ljava/lang/String;Z)V"); // eventProperty(String, boolean)
        env->CallStaticVoidMethod(clazz, mid, jprop, *(int*)prop->data);
        break;
    case MPV_FORMAT_INT64:
        mid = env->GetStaticMethodID(clazz, "eventProperty", "(Ljava/lang/String;J)V"); // eventProperty(String, long)
        env->CallStaticVoidMethod(clazz, mid, jprop, *(int64_t*)prop->data);
        break;
    case MPV_FORMAT_STRING:
        mid = env->GetStaticMethodID(clazz, "eventProperty", "(Ljava/lang/String;Ljava/lang/String;)V"); // eventProperty(String, String)
        jvalue = env->NewStringUTF(*(const char**)prop->data);
        env->CallStaticVoidMethod(clazz, mid, jprop, jvalue);
        break;
    }
}

static void sendEventToJava(JNIEnv *env, int event) {
    jclass clazz = env->FindClass("is/xyz/mpv/MPVLib");
    jmethodID mid = env->GetStaticMethodID(clazz, "event", "(I)V"); // event(int)
    env->CallStaticVoidMethod(clazz, mid, event);
}

jni_func(void, step) {
    while (1) {
        mpv_event *mp_event = mpv_wait_event(mpv, 0);
        mpv_event_property *mp_property = NULL;
        mpv_event_log_message *msg = NULL;
        if (mp_event->event_id == MPV_EVENT_NONE)
            break;
        switch (mp_event->event_id) {
        case MPV_EVENT_LOG_MESSAGE:
            msg = (mpv_event_log_message*)mp_event->data;
            ALOGV("[%s:%s] %s", msg->prefix, msg->level, msg->text);
            break;
        case MPV_EVENT_PROPERTY_CHANGE:
            mp_property = (mpv_event_property*)mp_event->data;
            sendPropertyUpdateToJava(env, mp_property);
            break;
        default:
            ALOGV("event: %s\n", mpv_event_name(mp_event->event_id));
            sendEventToJava(env, mp_event->event_id);
            break;
        }
    }
}

jni_func(jint, setOptionString, jstring joption, jstring jvalue) {
    if (!mpv)
        die("mpv is not initialized");

    const char *option = env->GetStringUTFChars(joption, NULL);
    const char *value = env->GetStringUTFChars(jvalue, NULL);

    int result = mpv_set_option_string(mpv, option, value);

    env->ReleaseStringUTFChars(joption, option);
    env->ReleaseStringUTFChars(jvalue, value);

    return result;
}

static int common_get_property(JNIEnv *env, jstring jproperty, mpv_format format, void *output) {
    if (!mpv)
        die("get_property called but mpv is not initialized");

    const char *prop = env->GetStringUTFChars(jproperty, NULL);
    int result = mpv_get_property(mpv, prop, format, output);
    if (result < 0)
        ALOGE("mpv_get_property(%s) format %d returned error %s", prop, format, mpv_error_string(result));
    env->ReleaseStringUTFChars(jproperty, prop);

    return result;
}

static int common_set_property(JNIEnv *env, jstring jproperty, mpv_format format, void *value) {
    if (!mpv)
        die("set_property called but mpv is not initialized");

    const char *prop = env->GetStringUTFChars(jproperty, NULL);
    int result = mpv_set_property(mpv, prop, format, value);
    if (result < 0)
        ALOGE("mpv_set_property(%s, %p) format %d returned error %s", prop, value, format, mpv_error_string(result));
    env->ReleaseStringUTFChars(jproperty, prop);

    return result;
}

jni_func(jobject, getPropertyInt, jstring jproperty) {
    int64_t value = 0;
    if (common_get_property(env, jproperty, MPV_FORMAT_INT64, &value) < 0)
        return NULL;
    return env->NewObject(java_Integer, java_Integer_init, (jint)value);
}

jni_func(jobject, getPropertyBoolean, jstring jproperty) {
    int value = 0;
    if (common_get_property(env, jproperty, MPV_FORMAT_FLAG, &value) < 0)
        return NULL;
    return env->NewObject(java_Boolean, java_Boolean_init, (jboolean)value);
}

jni_func(jstring, getPropertyString, jstring jproperty) {
    char *value;
    if (common_get_property(env, jproperty, MPV_FORMAT_STRING, &value) < 0)
        return NULL;
    jstring jvalue = env->NewStringUTF(value);
    mpv_free(value);
    return jvalue;
}

jni_func(void, setPropertyInt, jstring jproperty, jobject jvalue) {
    int64_t value = env->CallIntMethod(jvalue, java_Integer_intValue);
    common_set_property(env, jproperty, MPV_FORMAT_INT64, &value);
}

jni_func(void, setPropertyBoolean, jstring jproperty, jobject jvalue) {
    int value = env->CallBooleanMethod(jvalue, java_Boolean_booleanValue);
    common_set_property(env, jproperty, MPV_FORMAT_FLAG, &value);
}

jni_func(void, setPropertyString, jstring jproperty, jstring jvalue) {
    const char *value = env->GetStringUTFChars(jvalue, NULL);
    common_set_property(env, jproperty, MPV_FORMAT_STRING, &value);
    env->ReleaseStringUTFChars(jvalue, value);
}

jni_func(void, observeProperty, jstring property, jint format) {
    if (!mpv)
        die("mpv is not initialized");
    const char *prop = env->GetStringUTFChars(property, NULL);
    mpv_observe_property(mpv, 0, prop, (mpv_format)format);
    env->ReleaseStringUTFChars(property, prop);
}
<commit_msg>main: move mpv logging enabler to mpv context creation<commit_after>#include <jni.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <locale.h>

#include <mpv/client.h>
#include <mpv/opengl_cb.h>

#include <EGL/egl.h>

extern "C" {
    #include <libavcodec/jni.h>
}

#include "main.h"

#define ARRAYLEN(a) (sizeof(a)/sizeof(a[0]))
#define jni_func_name(name) Java_is_xyz_mpv_MPVLib_##name
#define jni_func(return_type, name, ...) JNIEXPORT return_type JNICALL jni_func_name(name) (JNIEnv *env, jobject obj, ##__VA_ARGS__)

extern "C" {
    jni_func(void, create);
    jni_func(void, init);
    jni_func(void, destroy);

    jni_func(void, initGL);
    jni_func(void, destroyGL);

    jni_func(void, command, jobjectArray jarray);

    jni_func(void, resize, jint width, jint height);
    jni_func(void, draw);
    jni_func(void, step);

    jni_func(jint, setOptionString, jstring option, jstring value);

    jni_func(jobject, getPropertyInt, jstring property);
    jni_func(void, setPropertyInt, jstring property, jobject value);
    jni_func(jobject, getPropertyBoolean, jstring property);
    jni_func(void, setPropertyBoolean, jstring property, jobject value);
    jni_func(jstring, getPropertyString, jstring jproperty);
    jni_func(void, setPropertyString, jstring jproperty, jstring jvalue);

    jni_func(void, observeProperty, jstring property, jint format);
};

mpv_handle *mpv;
mpv_opengl_cb_context *mpv_gl;
int g_width, g_height;

static void die(const char *msg)
{
    ALOGE("%s", msg);
    exit(1);
}

static void *get_proc_address_mpv(void *fn_ctx, const char *name)
{
    return (void*)eglGetProcAddress(name);
}

// Apparently it's considered slow to FindClass and GetMethodID every time we need them,
// so let's have a nice cache here
bool methods_initialized;
jclass java_Integer, java_Boolean;
jmethodID java_Integer_init, java_Integer_intValue, java_Boolean_init, java_Boolean_booleanValue;

static void init_methods_cache(JNIEnv *env) {
    if (methods_initialized)
        return;
    #define FIND_CLASS(name) reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(name)))
    java_Integer = FIND_CLASS("java/lang/Integer");
    java_Integer_init = env->GetMethodID(java_Integer, "<init>", "(I)V");
    java_Integer_intValue = env->GetMethodID(java_Integer, "intValue", "()I");
    java_Boolean = FIND_CLASS("java/lang/Boolean");
    java_Boolean_init = env->GetMethodID(java_Boolean, "<init>", "(Z)V");
    java_Boolean_booleanValue = env->GetMethodID(java_Boolean, "booleanValue", "()Z");
    #undef FIND_CLASS
    methods_initialized = true;
}

static void prepare_environment(JNIEnv *env) {
    setlocale(LC_NUMERIC, "C");

    JavaVM* vm = NULL;

    if (!env->GetJavaVM(&vm) && vm) {
        av_jni_set_java_vm(vm, NULL);
    }
    init_methods_cache(env);
}

jni_func(void, create) {
    prepare_environment(env);

    if (mpv)
        die("mpv is already initialized");

    mpv = mpv_create();
    if (!mpv)
        die("context init failed");

    mpv_request_log_messages(mpv, "v");
}

jni_func(void, init) {
    if (!mpv)
        die("mpv is not created");

    if (mpv_initialize(mpv) < 0)
        die("mpv init failed");
}

jni_func(void, destroy) {
    if (!mpv)
        die("mpv destroy called but it's already destroyed");
    mpv_terminate_destroy(mpv);
    mpv = NULL;
}

jni_func(void, initGL) {
    int ret = -1;
    if (!mpv)
        die("initGL: mpv not initialized");
    if (mpv_gl)
        die("OpenGL ES already initialized!?");

    mpv_gl = (mpv_opengl_cb_context*)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB);
    if (!mpv_gl)
        die("failed to create mpv GL API handle");

    if ((ret = mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address_mpv, NULL)) < 0) {
        ALOGE("mpv_opengl_cb_init_gl returned error %d", ret);
        die("failed to initialize mpv GL context");
    }
}

jni_func(void, destroyGL) {
    if (!mpv_gl)
        die("mpv_gl destroy called but it's already destroyed");
    mpv_opengl_cb_uninit_gl(mpv_gl);
    mpv_gl = NULL;
}

jni_func(void, command, jobjectArray jarray) {
    const char *arguments[128] = { 0 };
    int len = env->GetArrayLength(jarray);
    if (!mpv)
        die("Cannot run command: mpv is not initialized");
    if (len >= ARRAYLEN(arguments))
        die("Cannot run command: too many arguments");

    for (int i = 0; i < len; ++i)
        arguments[i] = env->GetStringUTFChars((jstring)env->GetObjectArrayElement(jarray, i), NULL);

    mpv_command(mpv, arguments);

    for (int i = 0; i < len; ++i)
        env->ReleaseStringUTFChars((jstring)env->GetObjectArrayElement(jarray, i), arguments[i]);
}

jni_func(void, resize, jint width, jint height) {
    ALOGV("Resizing! width: %d=>%d, %d=>%d\n", g_width, width, g_height, height);
    g_width = width;
    g_height = height;
}

jni_func(void, draw) {
    mpv_opengl_cb_draw(mpv_gl, 0, g_width, -g_height);
}

void sendPropertyUpdateToJava(JNIEnv *env, mpv_event_property *prop) {
    jmethodID mid;
    jstring jprop = env->NewStringUTF(prop->name);
    jclass clazz = env->FindClass("is/xyz/mpv/MPVLib");
    jstring jvalue = NULL;
    switch (prop->format) {
    case MPV_FORMAT_NONE:
        mid = env->GetStaticMethodID(clazz, "eventProperty", "(Ljava/lang/String;)V"); // eventProperty(String)
        env->CallStaticVoidMethod(clazz, mid, jprop);
        break;
    case MPV_FORMAT_FLAG:
        mid = env->GetStaticMethodID(clazz, "eventProperty", "(Ljava/lang/String;Z)V"); // eventProperty(String, boolean)
        env->CallStaticVoidMethod(clazz, mid, jprop, *(int*)prop->data);
        break;
    case MPV_FORMAT_INT64:
        mid = env->GetStaticMethodID(clazz, "eventProperty", "(Ljava/lang/String;J)V"); // eventProperty(String, long)
        env->CallStaticVoidMethod(clazz, mid, jprop, *(int64_t*)prop->data);
        break;
    case MPV_FORMAT_STRING:
        mid = env->GetStaticMethodID(clazz, "eventProperty", "(Ljava/lang/String;Ljava/lang/String;)V"); // eventProperty(String, String)
        jvalue = env->NewStringUTF(*(const char**)prop->data);
        env->CallStaticVoidMethod(clazz, mid, jprop, jvalue);
        break;
    }
}

static void sendEventToJava(JNIEnv *env, int event) {
    jclass clazz = env->FindClass("is/xyz/mpv/MPVLib");
    jmethodID mid = env->GetStaticMethodID(clazz, "event", "(I)V"); // event(int)
    env->CallStaticVoidMethod(clazz, mid, event);
}

jni_func(void, step) {
    while (1) {
        mpv_event *mp_event = mpv_wait_event(mpv, 0);
        mpv_event_property *mp_property = NULL;
        mpv_event_log_message *msg = NULL;
        if (mp_event->event_id == MPV_EVENT_NONE)
            break;
        switch (mp_event->event_id) {
        case MPV_EVENT_LOG_MESSAGE:
            msg = (mpv_event_log_message*)mp_event->data;
            ALOGV("[%s:%s] %s", msg->prefix, msg->level, msg->text);
            break;
        case MPV_EVENT_PROPERTY_CHANGE:
            mp_property = (mpv_event_property*)mp_event->data;
            sendPropertyUpdateToJava(env, mp_property);
            break;
        default:
            ALOGV("event: %s\n", mpv_event_name(mp_event->event_id));
            sendEventToJava(env, mp_event->event_id);
            break;
        }
    }
}

jni_func(jint, setOptionString, jstring joption, jstring jvalue) {
    if (!mpv)
        die("mpv is not initialized");

    const char *option = env->GetStringUTFChars(joption, NULL);
    const char *value = env->GetStringUTFChars(jvalue, NULL);

    int result = mpv_set_option_string(mpv, option, value);

    env->ReleaseStringUTFChars(joption, option);
    env->ReleaseStringUTFChars(jvalue, value);

    return result;
}

static int common_get_property(JNIEnv *env, jstring jproperty, mpv_format format, void *output) {
    if (!mpv)
        die("get_property called but mpv is not initialized");

    const char *prop = env->GetStringUTFChars(jproperty, NULL);
    int result = mpv_get_property(mpv, prop, format, output);
    if (result < 0)
        ALOGE("mpv_get_property(%s) format %d returned error %s", prop, format, mpv_error_string(result));
    env->ReleaseStringUTFChars(jproperty, prop);

    return result;
}

static int common_set_property(JNIEnv *env, jstring jproperty, mpv_format format, void *value) {
    if (!mpv)
        die("set_property called but mpv is not initialized");

    const char *prop = env->GetStringUTFChars(jproperty, NULL);
    int result = mpv_set_property(mpv, prop, format, value);
    if (result < 0)
        ALOGE("mpv_set_property(%s, %p) format %d returned error %s", prop, value, format, mpv_error_string(result));
    env->ReleaseStringUTFChars(jproperty, prop);

    return result;
}

jni_func(jobject, getPropertyInt, jstring jproperty) {
    int64_t value = 0;
    if (common_get_property(env, jproperty, MPV_FORMAT_INT64, &value) < 0)
        return NULL;
    return env->NewObject(java_Integer, java_Integer_init, (jint)value);
}

jni_func(jobject, getPropertyBoolean, jstring jproperty) {
    int value = 0;
    if (common_get_property(env, jproperty, MPV_FORMAT_FLAG, &value) < 0)
        return NULL;
    return env->NewObject(java_Boolean, java_Boolean_init, (jboolean)value);
}

jni_func(jstring, getPropertyString, jstring jproperty) {
    char *value;
    if (common_get_property(env, jproperty, MPV_FORMAT_STRING, &value) < 0)
        return NULL;
    jstring jvalue = env->NewStringUTF(value);
    mpv_free(value);
    return jvalue;
}

jni_func(void, setPropertyInt, jstring jproperty, jobject jvalue) {
    int64_t value = env->CallIntMethod(jvalue, java_Integer_intValue);
    common_set_property(env, jproperty, MPV_FORMAT_INT64, &value);
}

jni_func(void, setPropertyBoolean, jstring jproperty, jobject jvalue) {
    int value = env->CallBooleanMethod(jvalue, java_Boolean_booleanValue);
    common_set_property(env, jproperty, MPV_FORMAT_FLAG, &value);
}

jni_func(void, setPropertyString, jstring jproperty, jstring jvalue) {
    const char *value = env->GetStringUTFChars(jvalue, NULL);
    common_set_property(env, jproperty, MPV_FORMAT_STRING, &value);
    env->ReleaseStringUTFChars(jvalue, value);
}

jni_func(void, observeProperty, jstring property, jint format) {
    if (!mpv)
        die("mpv is not initialized");
    const char *prop = env->GetStringUTFChars(property, NULL);
    mpv_observe_property(mpv, 0, prop, (mpv_format)format);
    env->ReleaseStringUTFChars(property, prop);
}
<|endoftext|>


<commit_before>#include <jni.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <locale.h>

#include <mpv/client.h>
#include <mpv/opengl_cb.h>

#include <EGL/egl.h>

extern "C" {
    #include <libavcodec/jni.h>
}

#include "main.h"

extern void android_content_init(JNIEnv *env, jobject appctx);
extern void android_content_register(mpv_handle *mpv);

#define ARRAYLEN(a) (sizeof(a)/sizeof(a[0]))
#define jni_func_name(name) Java_is_xyz_mpv_MPVLib_##name
#define jni_func(return_type, name, ...) JNIEXPORT return_type JNICALL jni_func_name(name) (JNIEnv *env, jobject obj, ##__VA_ARGS__)

extern "C" {
    jni_func(void, create, jobject appctx);
    jni_func(void, init);
    jni_func(void, destroy);

    jni_func(void, initGL, jobject view);
    jni_func(void, destroyGL);

    jni_func(void, command, jobjectArray jarray);

    jni_func(void, resize, jint width, jint height);
    jni_func(void, draw);
    jni_func(void, step);

    jni_func(jint, setOptionString, jstring option, jstring value);

    jni_func(jobject, getPropertyInt, jstring property);
    jni_func(void, setPropertyInt, jstring property, jobject value);
    jni_func(jobject, getPropertyBoolean, jstring property);
    jni_func(void, setPropertyBoolean, jstring property, jobject value);
    jni_func(jstring, getPropertyString, jstring jproperty);
    jni_func(void, setPropertyString, jstring jproperty, jstring jvalue);

    jni_func(void, observeProperty, jstring property, jint format);
};

mpv_handle *mpv;
mpv_opengl_cb_context *mpv_gl;
int g_width, g_height;

static void die(const char *msg)
{
    ALOGE("%s", msg);
    exit(1);
}

static void *get_proc_address_mpv(void *fn_ctx, const char *name)
{
    return (void*)eglGetProcAddress(name);
}

// Apparently it's considered slow to FindClass and GetMethodID every time we need them,
// so let's have a nice cache here
bool methods_initialized;
jclass java_Integer, java_Boolean;
jmethodID java_Integer_init, java_Integer_intValue, java_Boolean_init, java_Boolean_booleanValue;
jmethodID java_GLSurfaceView_requestRender;

static void init_methods_cache(JNIEnv *env) {
    if (methods_initialized)
        return;
    #define FIND_CLASS(name) reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(name)))
    java_Integer = FIND_CLASS("java/lang/Integer");
    java_Integer_init = env->GetMethodID(java_Integer, "<init>", "(I)V");
    java_Integer_intValue = env->GetMethodID(java_Integer, "intValue", "()I");
    java_Boolean = FIND_CLASS("java/lang/Boolean");
    java_Boolean_init = env->GetMethodID(java_Boolean, "<init>", "(Z)V");
    java_Boolean_booleanValue = env->GetMethodID(java_Boolean, "booleanValue", "()Z");
    jclass java_GLSurfaceView = FIND_CLASS("android/opengl/GLSurfaceView");
    java_GLSurfaceView_requestRender = env->GetMethodID(java_GLSurfaceView, "requestRender", "()V");
    #undef FIND_CLASS
    methods_initialized = true;
}

static void *render_cb(void *data)
{
    jobject *glView = (void *)data;
    return env->CallVoidMethod(glView, java_GLSurfaceView_requestRender);
}

static void prepare_environment(JNIEnv *env, jobject appctx) {
    setlocale(LC_NUMERIC, "C");

    JavaVM* vm = NULL;

    if (!env->GetJavaVM(&vm) && vm) {
        av_jni_set_java_vm(vm, NULL);
    }
    init_methods_cache(env);
    android_content_init(env, appctx);
}

jni_func(void, create, jobject appctx) {
    prepare_environment(env, appctx);

    if (mpv)
        die("mpv is already initialized");

    mpv = mpv_create();
    if (!mpv)
        die("context init failed");

    mpv_request_log_messages(mpv, "v");
}

jni_func(void, init) {
    if (!mpv)
        die("mpv is not created");

    if (mpv_initialize(mpv) < 0)
        die("mpv init failed");

    android_content_register(mpv);
#ifdef __aarch64__
    ALOGV("You're using the 64-bit build of mpv!");
#endif
}

jni_func(void, destroy) {
    if (!mpv)
        die("mpv destroy called but it's already destroyed");
    mpv_terminate_destroy(mpv);
    mpv = NULL;
}

jni_func(void, initGL, jobject view) {
    int ret = -1;
    if (!mpv)
        die("initGL: mpv not initialized");
    if (mpv_gl)
        die("OpenGL ES already initialized!?");

    mpv_gl = (mpv_opengl_cb_context*)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB);
    if (!mpv_gl)
        die("failed to create mpv GL API handle");

    if ((ret = mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address_mpv, NULL)) < 0) {
        ALOGE("mpv_opengl_cb_init_gl returned error %d", ret);
        die("failed to initialize mpv GL context");
    }

    mpv_opengl_cb_set_update_callback(mpv_gl, render_cb, (void *)view);
}

jni_func(void, destroyGL) {
    if (!mpv_gl)
        die("mpv_gl destroy called but it's already destroyed");
    mpv_opengl_cb_uninit_gl(mpv_gl);
    mpv_gl = NULL;
}

jni_func(void, command, jobjectArray jarray) {
    const char *arguments[128] = { 0 };
    int len = env->GetArrayLength(jarray);
    if (!mpv)
        die("Cannot run command: mpv is not initialized");
    if (len >= ARRAYLEN(arguments))
        die("Cannot run command: too many arguments");

    for (int i = 0; i < len; ++i)
        arguments[i] = env->GetStringUTFChars((jstring)env->GetObjectArrayElement(jarray, i), NULL);

    mpv_command(mpv, arguments);

    for (int i = 0; i < len; ++i)
        env->ReleaseStringUTFChars((jstring)env->GetObjectArrayElement(jarray, i), arguments[i]);
}

jni_func(void, resize, jint width, jint height) {
    ALOGV("Resizing! width: %d=>%d, %d=>%d\n", g_width, width, g_height, height);
    g_width = width;
    g_height = height;
}

jni_func(void, draw) {
    mpv_opengl_cb_draw(mpv_gl, 0, g_width, -g_height);
}

void sendPropertyUpdateToJava(JNIEnv *env, mpv_event_property *prop) {
    jmethodID mid;
    jstring jprop = env->NewStringUTF(prop->name);
    jclass clazz = env->FindClass("is/xyz/mpv/MPVLib");
    jstring jvalue = NULL;
    switch (prop->format) {
    case MPV_FORMAT_NONE:
        mid = env->GetStaticMethodID(clazz, "eventProperty", "(Ljava/lang/String;)V"); // eventProperty(String)
        env->CallStaticVoidMethod(clazz, mid, jprop);
        break;
    case MPV_FORMAT_FLAG:
        mid = env->GetStaticMethodID(clazz, "eventProperty", "(Ljava/lang/String;Z)V"); // eventProperty(String, boolean)
        env->CallStaticVoidMethod(clazz, mid, jprop, *(int*)prop->data);
        break;
    case MPV_FORMAT_INT64:
        mid = env->GetStaticMethodID(clazz, "eventProperty", "(Ljava/lang/String;J)V"); // eventProperty(String, long)
        env->CallStaticVoidMethod(clazz, mid, jprop, *(int64_t*)prop->data);
        break;
    case MPV_FORMAT_STRING:
        mid = env->GetStaticMethodID(clazz, "eventProperty", "(Ljava/lang/String;Ljava/lang/String;)V"); // eventProperty(String, String)
        jvalue = env->NewStringUTF(*(const char**)prop->data);
        env->CallStaticVoidMethod(clazz, mid, jprop, jvalue);
        break;
    default:
        ALOGV("sendPropertyUpdateToJava: Unknown property update format received in callback: %d!", prop->format);
        break;
    }
}

static void sendEventToJava(JNIEnv *env, int event) {
    jclass clazz = env->FindClass("is/xyz/mpv/MPVLib");
    jmethodID mid = env->GetStaticMethodID(clazz, "event", "(I)V"); // event(int)
    env->CallStaticVoidMethod(clazz, mid, event);
}

jni_func(void, step) {
    while (1) {
        mpv_event *mp_event = mpv_wait_event(mpv, 0);
        mpv_event_property *mp_property = NULL;
        mpv_event_log_message *msg = NULL;
        if (mp_event->event_id == MPV_EVENT_NONE)
            break;
        switch (mp_event->event_id) {
        case MPV_EVENT_LOG_MESSAGE:
            msg = (mpv_event_log_message*)mp_event->data;
            ALOGV("[%s:%s] %s", msg->prefix, msg->level, msg->text);
            break;
        case MPV_EVENT_PROPERTY_CHANGE:
            mp_property = (mpv_event_property*)mp_event->data;
            sendPropertyUpdateToJava(env, mp_property);
            break;
        default:
            ALOGV("event: %s\n", mpv_event_name(mp_event->event_id));
            sendEventToJava(env, mp_event->event_id);
            break;
        }
    }
}

jni_func(jint, setOptionString, jstring joption, jstring jvalue) {
    if (!mpv)
        die("mpv is not initialized");

    const char *option = env->GetStringUTFChars(joption, NULL);
    const char *value = env->GetStringUTFChars(jvalue, NULL);

    int result = mpv_set_option_string(mpv, option, value);

    env->ReleaseStringUTFChars(joption, option);
    env->ReleaseStringUTFChars(jvalue, value);

    return result;
}

static int common_get_property(JNIEnv *env, jstring jproperty, mpv_format format, void *output) {
    if (!mpv)
        die("get_property called but mpv is not initialized");

    const char *prop = env->GetStringUTFChars(jproperty, NULL);
    int result = mpv_get_property(mpv, prop, format, output);
    if (result < 0)
        ALOGE("mpv_get_property(%s) format %d returned error %s", prop, format, mpv_error_string(result));
    env->ReleaseStringUTFChars(jproperty, prop);

    return result;
}

static int common_set_property(JNIEnv *env, jstring jproperty, mpv_format format, void *value) {
    if (!mpv)
        die("set_property called but mpv is not initialized");

    const char *prop = env->GetStringUTFChars(jproperty, NULL);
    int result = mpv_set_property(mpv, prop, format, value);
    if (result < 0)
        ALOGE("mpv_set_property(%s, %p) format %d returned error %s", prop, value, format, mpv_error_string(result));
    env->ReleaseStringUTFChars(jproperty, prop);

    return result;
}

jni_func(jobject, getPropertyInt, jstring jproperty) {
    int64_t value = 0;
    if (common_get_property(env, jproperty, MPV_FORMAT_INT64, &value) < 0)
        return NULL;
    return env->NewObject(java_Integer, java_Integer_init, (jint)value);
}

jni_func(jobject, getPropertyBoolean, jstring jproperty) {
    int value = 0;
    if (common_get_property(env, jproperty, MPV_FORMAT_FLAG, &value) < 0)
        return NULL;
    return env->NewObject(java_Boolean, java_Boolean_init, (jboolean)value);
}

jni_func(jstring, getPropertyString, jstring jproperty) {
    char *value;
    if (common_get_property(env, jproperty, MPV_FORMAT_STRING, &value) < 0)
        return NULL;
    jstring jvalue = env->NewStringUTF(value);
    mpv_free(value);
    return jvalue;
}

jni_func(void, setPropertyInt, jstring jproperty, jobject jvalue) {
    int64_t value = env->CallIntMethod(jvalue, java_Integer_intValue);
    common_set_property(env, jproperty, MPV_FORMAT_INT64, &value);
}

jni_func(void, setPropertyBoolean, jstring jproperty, jobject jvalue) {
    int value = env->CallBooleanMethod(jvalue, java_Boolean_booleanValue);
    common_set_property(env, jproperty, MPV_FORMAT_FLAG, &value);
}

jni_func(void, setPropertyString, jstring jproperty, jstring jvalue) {
    const char *value = env->GetStringUTFChars(jvalue, NULL);
    common_set_property(env, jproperty, MPV_FORMAT_STRING, &value);
    env->ReleaseStringUTFChars(jvalue, value);
}

jni_func(void, observeProperty, jstring property, jint format) {
    if (!mpv)
        die("mpv is not initialized");
    const char *prop = env->GetStringUTFChars(property, NULL);
    mpv_observe_property(mpv, 0, prop, (mpv_format)format);
    env->ReleaseStringUTFChars(property, prop);
}
<commit_msg>use a simple FindClass<commit_after>#include <jni.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <locale.h>

#include <mpv/client.h>
#include <mpv/opengl_cb.h>

#include <EGL/egl.h>

extern "C" {
    #include <libavcodec/jni.h>
}

#include "main.h"

extern void android_content_init(JNIEnv *env, jobject appctx);
extern void android_content_register(mpv_handle *mpv);

#define ARRAYLEN(a) (sizeof(a)/sizeof(a[0]))
#define jni_func_name(name) Java_is_xyz_mpv_MPVLib_##name
#define jni_func(return_type, name, ...) JNIEXPORT return_type JNICALL jni_func_name(name) (JNIEnv *env, jobject obj, ##__VA_ARGS__)

extern "C" {
    jni_func(void, create, jobject appctx);
    jni_func(void, init);
    jni_func(void, destroy);

    jni_func(void, initGL, jobject view);
    jni_func(void, destroyGL);

    jni_func(void, command, jobjectArray jarray);

    jni_func(void, resize, jint width, jint height);
    jni_func(void, draw);
    jni_func(void, step);

    jni_func(jint, setOptionString, jstring option, jstring value);

    jni_func(jobject, getPropertyInt, jstring property);
    jni_func(void, setPropertyInt, jstring property, jobject value);
    jni_func(jobject, getPropertyBoolean, jstring property);
    jni_func(void, setPropertyBoolean, jstring property, jobject value);
    jni_func(jstring, getPropertyString, jstring jproperty);
    jni_func(void, setPropertyString, jstring jproperty, jstring jvalue);

    jni_func(void, observeProperty, jstring property, jint format);
};

mpv_handle *mpv;
mpv_opengl_cb_context *mpv_gl;
int g_width, g_height;

static void die(const char *msg)
{
    ALOGE("%s", msg);
    exit(1);
}

static void *get_proc_address_mpv(void *fn_ctx, const char *name)
{
    return (void*)eglGetProcAddress(name);
}

// Apparently it's considered slow to FindClass and GetMethodID every time we need them,
// so let's have a nice cache here
bool methods_initialized;
jclass java_Integer, java_Boolean;
jmethodID java_Integer_init, java_Integer_intValue, java_Boolean_init, java_Boolean_booleanValue;
jmethodID java_GLSurfaceView_requestRender;

static void init_methods_cache(JNIEnv *env) {
    if (methods_initialized)
        return;
    #define FIND_CLASS(name) reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(name)))
    java_Integer = FIND_CLASS("java/lang/Integer");
    java_Integer_init = env->GetMethodID(java_Integer, "<init>", "(I)V");
    java_Integer_intValue = env->GetMethodID(java_Integer, "intValue", "()I");
    java_Boolean = FIND_CLASS("java/lang/Boolean");
    java_Boolean_init = env->GetMethodID(java_Boolean, "<init>", "(Z)V");
    java_Boolean_booleanValue = env->GetMethodID(java_Boolean, "booleanValue", "()Z");
    #undef FIND_CLASS
    jclass java_GLSurfaceView = env->FindClass("android/opengl/GLSurfaceView");
    java_GLSurfaceView_requestRender = env->GetMethodID(java_GLSurfaceView, "requestRender", "()V");
    methods_initialized = true;
}

static void *render_cb(void *data)
{
    jobject *glView = (void *)data;
    return env->CallVoidMethod(glView, java_GLSurfaceView_requestRender);
}

static void prepare_environment(JNIEnv *env, jobject appctx) {
    setlocale(LC_NUMERIC, "C");

    JavaVM* vm = NULL;

    if (!env->GetJavaVM(&vm) && vm) {
        av_jni_set_java_vm(vm, NULL);
    }
    init_methods_cache(env);
    android_content_init(env, appctx);
}

jni_func(void, create, jobject appctx) {
    prepare_environment(env, appctx);

    if (mpv)
        die("mpv is already initialized");

    mpv = mpv_create();
    if (!mpv)
        die("context init failed");

    mpv_request_log_messages(mpv, "v");
}

jni_func(void, init) {
    if (!mpv)
        die("mpv is not created");

    if (mpv_initialize(mpv) < 0)
        die("mpv init failed");

    android_content_register(mpv);
#ifdef __aarch64__
    ALOGV("You're using the 64-bit build of mpv!");
#endif
}

jni_func(void, destroy) {
    if (!mpv)
        die("mpv destroy called but it's already destroyed");
    mpv_terminate_destroy(mpv);
    mpv = NULL;
}

jni_func(void, initGL, jobject view) {
    int ret = -1;
    if (!mpv)
        die("initGL: mpv not initialized");
    if (mpv_gl)
        die("OpenGL ES already initialized!?");

    mpv_gl = (mpv_opengl_cb_context*)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB);
    if (!mpv_gl)
        die("failed to create mpv GL API handle");

    if ((ret = mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address_mpv, NULL)) < 0) {
        ALOGE("mpv_opengl_cb_init_gl returned error %d", ret);
        die("failed to initialize mpv GL context");
    }

    mpv_opengl_cb_set_update_callback(mpv_gl, render_cb, (void *)view);
}

jni_func(void, destroyGL) {
    if (!mpv_gl)
        die("mpv_gl destroy called but it's already destroyed");
    mpv_opengl_cb_uninit_gl(mpv_gl);
    mpv_gl = NULL;
}

jni_func(void, command, jobjectArray jarray) {
    const char *arguments[128] = { 0 };
    int len = env->GetArrayLength(jarray);
    if (!mpv)
        die("Cannot run command: mpv is not initialized");
    if (len >= ARRAYLEN(arguments))
        die("Cannot run command: too many arguments");

    for (int i = 0; i < len; ++i)
        arguments[i] = env->GetStringUTFChars((jstring)env->GetObjectArrayElement(jarray, i), NULL);

    mpv_command(mpv, arguments);

    for (int i = 0; i < len; ++i)
        env->ReleaseStringUTFChars((jstring)env->GetObjectArrayElement(jarray, i), arguments[i]);
}

jni_func(void, resize, jint width, jint height) {
    ALOGV("Resizing! width: %d=>%d, %d=>%d\n", g_width, width, g_height, height);
    g_width = width;
    g_height = height;
}

jni_func(void, draw) {
    mpv_opengl_cb_draw(mpv_gl, 0, g_width, -g_height);
}

void sendPropertyUpdateToJava(JNIEnv *env, mpv_event_property *prop) {
    jmethodID mid;
    jstring jprop = env->NewStringUTF(prop->name);
    jclass clazz = env->FindClass("is/xyz/mpv/MPVLib");
    jstring jvalue = NULL;
    switch (prop->format) {
    case MPV_FORMAT_NONE:
        mid = env->GetStaticMethodID(clazz, "eventProperty", "(Ljava/lang/String;)V"); // eventProperty(String)
        env->CallStaticVoidMethod(clazz, mid, jprop);
        break;
    case MPV_FORMAT_FLAG:
        mid = env->GetStaticMethodID(clazz, "eventProperty", "(Ljava/lang/String;Z)V"); // eventProperty(String, boolean)
        env->CallStaticVoidMethod(clazz, mid, jprop, *(int*)prop->data);
        break;
    case MPV_FORMAT_INT64:
        mid = env->GetStaticMethodID(clazz, "eventProperty", "(Ljava/lang/String;J)V"); // eventProperty(String, long)
        env->CallStaticVoidMethod(clazz, mid, jprop, *(int64_t*)prop->data);
        break;
    case MPV_FORMAT_STRING:
        mid = env->GetStaticMethodID(clazz, "eventProperty", "(Ljava/lang/String;Ljava/lang/String;)V"); // eventProperty(String, String)
        jvalue = env->NewStringUTF(*(const char**)prop->data);
        env->CallStaticVoidMethod(clazz, mid, jprop, jvalue);
        break;
    default:
        ALOGV("sendPropertyUpdateToJava: Unknown property update format received in callback: %d!", prop->format);
        break;
    }
}

static void sendEventToJava(JNIEnv *env, int event) {
    jclass clazz = env->FindClass("is/xyz/mpv/MPVLib");
    jmethodID mid = env->GetStaticMethodID(clazz, "event", "(I)V"); // event(int)
    env->CallStaticVoidMethod(clazz, mid, event);
}

jni_func(void, step) {
    while (1) {
        mpv_event *mp_event = mpv_wait_event(mpv, 0);
        mpv_event_property *mp_property = NULL;
        mpv_event_log_message *msg = NULL;
        if (mp_event->event_id == MPV_EVENT_NONE)
            break;
        switch (mp_event->event_id) {
        case MPV_EVENT_LOG_MESSAGE:
            msg = (mpv_event_log_message*)mp_event->data;
            ALOGV("[%s:%s] %s", msg->prefix, msg->level, msg->text);
            break;
        case MPV_EVENT_PROPERTY_CHANGE:
            mp_property = (mpv_event_property*)mp_event->data;
            sendPropertyUpdateToJava(env, mp_property);
            break;
        default:
            ALOGV("event: %s\n", mpv_event_name(mp_event->event_id));
            sendEventToJava(env, mp_event->event_id);
            break;
        }
    }
}

jni_func(jint, setOptionString, jstring joption, jstring jvalue) {
    if (!mpv)
        die("mpv is not initialized");

    const char *option = env->GetStringUTFChars(joption, NULL);
    const char *value = env->GetStringUTFChars(jvalue, NULL);

    int result = mpv_set_option_string(mpv, option, value);

    env->ReleaseStringUTFChars(joption, option);
    env->ReleaseStringUTFChars(jvalue, value);

    return result;
}

static int common_get_property(JNIEnv *env, jstring jproperty, mpv_format format, void *output) {
    if (!mpv)
        die("get_property called but mpv is not initialized");

    const char *prop = env->GetStringUTFChars(jproperty, NULL);
    int result = mpv_get_property(mpv, prop, format, output);
    if (result < 0)
        ALOGE("mpv_get_property(%s) format %d returned error %s", prop, format, mpv_error_string(result));
    env->ReleaseStringUTFChars(jproperty, prop);

    return result;
}

static int common_set_property(JNIEnv *env, jstring jproperty, mpv_format format, void *value) {
    if (!mpv)
        die("set_property called but mpv is not initialized");

    const char *prop = env->GetStringUTFChars(jproperty, NULL);
    int result = mpv_set_property(mpv, prop, format, value);
    if (result < 0)
        ALOGE("mpv_set_property(%s, %p) format %d returned error %s", prop, value, format, mpv_error_string(result));
    env->ReleaseStringUTFChars(jproperty, prop);

    return result;
}

jni_func(jobject, getPropertyInt, jstring jproperty) {
    int64_t value = 0;
    if (common_get_property(env, jproperty, MPV_FORMAT_INT64, &value) < 0)
        return NULL;
    return env->NewObject(java_Integer, java_Integer_init, (jint)value);
}

jni_func(jobject, getPropertyBoolean, jstring jproperty) {
    int value = 0;
    if (common_get_property(env, jproperty, MPV_FORMAT_FLAG, &value) < 0)
        return NULL;
    return env->NewObject(java_Boolean, java_Boolean_init, (jboolean)value);
}

jni_func(jstring, getPropertyString, jstring jproperty) {
    char *value;
    if (common_get_property(env, jproperty, MPV_FORMAT_STRING, &value) < 0)
        return NULL;
    jstring jvalue = env->NewStringUTF(value);
    mpv_free(value);
    return jvalue;
}

jni_func(void, setPropertyInt, jstring jproperty, jobject jvalue) {
    int64_t value = env->CallIntMethod(jvalue, java_Integer_intValue);
    common_set_property(env, jproperty, MPV_FORMAT_INT64, &value);
}

jni_func(void, setPropertyBoolean, jstring jproperty, jobject jvalue) {
    int value = env->CallBooleanMethod(jvalue, java_Boolean_booleanValue);
    common_set_property(env, jproperty, MPV_FORMAT_FLAG, &value);
}

jni_func(void, setPropertyString, jstring jproperty, jstring jvalue) {
    const char *value = env->GetStringUTFChars(jvalue, NULL);
    common_set_property(env, jproperty, MPV_FORMAT_STRING, &value);
    env->ReleaseStringUTFChars(jvalue, value);
}

jni_func(void, observeProperty, jstring property, jint format) {
    if (!mpv)
        die("mpv is not initialized");
    const char *prop = env->GetStringUTFChars(property, NULL);
    mpv_observe_property(mpv, 0, prop, (mpv_format)format);
    env->ReleaseStringUTFChars(property, prop);
}
<|endoftext|>