b3904a718b
rename app -> mpApp and initial handling of frames, we will need a mini window-manager in here, most likely.
369 lines
12 KiB
C++
369 lines
12 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* Version: MPL 1.1 / GPLv3+ / LGPLv3+
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License or as specified alternatively below. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* Major Contributor(s):
|
|
* Copyright (C) 2012 Novell, Inc.
|
|
* Michael Meeks <michael.meeks@suse.com> (initial developer)
|
|
*
|
|
* All Rights Reserved.
|
|
*
|
|
* For minor contributions see the git repository.
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 3 or later (the "GPLv3+"), or
|
|
* the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
|
|
* in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
|
|
* instead of those above.
|
|
*/
|
|
#include <android/androidinst.hxx>
|
|
#include <headless/svpdummies.hxx>
|
|
#include <generic/gendata.hxx>
|
|
#include <android/log.h>
|
|
#include <android/looper.h>
|
|
#include <lo-bootstrap.h>
|
|
#include <osl/detail/android_native_app_glue.h>
|
|
#include <rtl/strbuf.hxx>
|
|
|
|
class AndroidSalData : public SalGenericData
|
|
{
|
|
public:
|
|
AndroidSalData( SalInstance *pInstance ) : SalGenericData( SAL_DATA_ANDROID, pInstance ) {}
|
|
virtual void ErrorTrapPush() {}
|
|
virtual bool ErrorTrapPop( bool ) { return false; }
|
|
};
|
|
|
|
static rtl::OString MotionEdgeFlagsToString(int32_t nFlags)
|
|
{
|
|
rtl::OStringBuffer aStr;
|
|
if (nFlags == AMOTION_EVENT_EDGE_FLAG_NONE)
|
|
aStr.append ("no-edge");
|
|
if (nFlags & AMOTION_EVENT_EDGE_FLAG_TOP)
|
|
aStr.append (":top");
|
|
if (nFlags & AMOTION_EVENT_EDGE_FLAG_BOTTOM)
|
|
aStr.append (":bottom");
|
|
if (nFlags & AMOTION_EVENT_EDGE_FLAG_LEFT)
|
|
aStr.append (":left");
|
|
if (nFlags & AMOTION_EVENT_EDGE_FLAG_RIGHT)
|
|
aStr.append (":right");
|
|
return aStr.makeStringAndClear();
|
|
}
|
|
|
|
static rtl::OString KeyMetaStateToString(int32_t nFlags)
|
|
{
|
|
rtl::OStringBuffer aStr;
|
|
if (nFlags == AMETA_NONE)
|
|
aStr.append ("no-meta");
|
|
if (nFlags & AMETA_ALT_ON)
|
|
aStr.append (":alt");
|
|
if (nFlags & AMETA_SHIFT_ON)
|
|
aStr.append (":shift");
|
|
if (nFlags & AMETA_SYM_ON)
|
|
aStr.append (":sym");
|
|
return aStr.makeStringAndClear();
|
|
}
|
|
|
|
static void BlitFrameRegionToWindow(ANativeWindow *pWindow,
|
|
const basebmp::BitmapDeviceSharedPtr& /* aDev */,
|
|
const ARect &aSrcRect,
|
|
int nDestX, int nDestY)
|
|
{
|
|
fprintf (stderr, "Blit frame src %d,%d->%d,%d to positoon %d, %d\n",
|
|
aSrcRect.left, aSrcRect.top, aSrcRect.right, aSrcRect.bottom,
|
|
nDestX, nDestY);
|
|
#if 1
|
|
ARect aRect;
|
|
ANativeWindow_Buffer aBuffer;
|
|
memset ((void *)&aBuffer, 0, sizeof (aBuffer));
|
|
int32_t nRet = ANativeWindow_lock(pWindow, &aBuffer, &aRect);
|
|
fprintf (stderr, "locked window %d returned rect: %d,%d->%d,%d "
|
|
"buffer: %dx%d stride %d, format %d, bits %p\n",
|
|
nRet, aRect.left, aRect.top, aRect.right, aRect.bottom,
|
|
aBuffer.width, aBuffer.height, aBuffer.stride,
|
|
aBuffer.format, aBuffer.bits);
|
|
if (aBuffer.bits)
|
|
{
|
|
// hard-code / guess at a format ...
|
|
int32_t *p = (int32_t *)aBuffer.bits;
|
|
for (int32_t y = 0; y < aBuffer.height; y++)
|
|
{
|
|
for (int32_t x = 0; x < aBuffer.stride / 4; x++)
|
|
*p++ = (y << 24) + x;
|
|
}
|
|
}
|
|
ANativeWindow_unlockAndPost(pWindow);
|
|
fprintf (stderr, "done render!\n");
|
|
#endif
|
|
|
|
}
|
|
|
|
void AndroidSalInstance::BlitFrameToWindow(ANativeWindow *pWindow,
|
|
const basebmp::BitmapDeviceSharedPtr& aDev)
|
|
{
|
|
basegfx::B2IVector aDevSize = aDev->getSize();
|
|
ARect aWhole = { 0, 0, aDevSize.getX(), aDevSize.getY() };
|
|
BlitFrameRegionToWindow(pWindow, aDev, aWhole, 0, 0);
|
|
}
|
|
|
|
void AndroidSalInstance::RedrawWindows(ANativeWindow *pWindow)
|
|
{
|
|
std::list< SalFrame* >::const_iterator it;
|
|
for ( it = getFrames().begin(); it != getFrames().end(); it++ )
|
|
{
|
|
SvpSalFrame *pFrame = static_cast<SvpSalFrame *>(*it);
|
|
BlitFrameToWindow (pWindow, pFrame->getDevice());
|
|
}
|
|
}
|
|
|
|
void AndroidSalInstance::onAppCmd (struct android_app* app, int32_t cmd)
|
|
{
|
|
fprintf (stderr, "app cmd for app %p, cmd %d\n", app, cmd);
|
|
ANativeWindow *pWindow = mpApp->window;
|
|
switch (cmd) {
|
|
case APP_CMD_INIT_WINDOW:
|
|
{
|
|
ARect aRect = { 0, 0, 0, 0 };
|
|
aRect.right = ANativeWindow_getWidth(pWindow);
|
|
aRect.bottom = ANativeWindow_getHeight(pWindow);
|
|
fprintf (stderr, "we have an app window ! %p %dx%x (%d)\n",
|
|
pWindow, aRect.right, aRect.bottom,
|
|
ANativeWindow_getFormat(pWindow));
|
|
|
|
RedrawWindows(pWindow);
|
|
break;
|
|
}
|
|
case APP_CMD_WINDOW_RESIZED:
|
|
{
|
|
ARect aRect = { 0, 0, 0, 0 };
|
|
aRect.right = ANativeWindow_getWidth(pWindow);
|
|
aRect.bottom = ANativeWindow_getHeight(pWindow);
|
|
fprintf (stderr, "app window resized to ! %p %dx%x (%d)\n",
|
|
pWindow, aRect.right, aRect.bottom,
|
|
ANativeWindow_getFormat(pWindow));
|
|
RedrawWindows(pWindow);
|
|
break;
|
|
}
|
|
|
|
case APP_CMD_WINDOW_REDRAW_NEEDED:
|
|
{
|
|
RedrawWindows(pWindow);
|
|
break;
|
|
}
|
|
|
|
case APP_CMD_CONTENT_RECT_CHANGED:
|
|
{
|
|
ARect aRect = mpApp->contentRect;
|
|
fprintf (stderr, "content rect changed [ k/b popped up etc. ] %d,%d->%d,%d\n",
|
|
aRect.left, aRect.top, aRect.right, aRect.bottom);
|
|
break;
|
|
}
|
|
default:
|
|
fprintf (stderr, "unhandled app cmd %d\n", cmd);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int32_t AndroidSalInstance::onInputEvent (struct android_app* app, AInputEvent* event)
|
|
{
|
|
fprintf (stderr, "input event for app %p, event %p type %d source %d device id %d\n",
|
|
app, event,
|
|
AInputEvent_getType(event),
|
|
AInputEvent_getSource(event),
|
|
AInputEvent_getDeviceId(event));
|
|
|
|
switch (AInputEvent_getType(event))
|
|
{
|
|
case AINPUT_EVENT_TYPE_KEY:
|
|
{
|
|
int32_t nAction = AKeyEvent_getAction(event);
|
|
fprintf (stderr, "key event keycode %d '%s' %s\n",
|
|
AKeyEvent_getKeyCode(event),
|
|
nAction == AKEY_EVENT_ACTION_DOWN ? "down" :
|
|
nAction == AKEY_EVENT_ACTION_UP ? "up" : "multiple",
|
|
KeyMetaStateToString(AKeyEvent_getMetaState(event)).getStr());
|
|
break;
|
|
}
|
|
case AINPUT_EVENT_TYPE_MOTION:
|
|
{
|
|
fprintf (stderr, "motion event %d %g %g %s\n",
|
|
AMotionEvent_getAction(event),
|
|
AMotionEvent_getXOffset(event),
|
|
AMotionEvent_getYOffset(event),
|
|
MotionEdgeFlagsToString(AMotionEvent_getEdgeFlags(event)).getStr());
|
|
break;
|
|
}
|
|
default:
|
|
fprintf (stderr, "unknown event type %p %d\n",
|
|
event, AInputEvent_getType(event));
|
|
}
|
|
return 1; // handled 0 for not ...
|
|
}
|
|
|
|
AndroidSalInstance *AndroidSalInstance::getInstance()
|
|
{
|
|
AndroidSalData *pData = static_cast<AndroidSalData *>(ImplGetSVData()->mpSalData);
|
|
if (!pData)
|
|
return NULL;
|
|
return static_cast<AndroidSalInstance *>(pData->m_pInstance);
|
|
}
|
|
|
|
extern "C" {
|
|
void onAppCmd_cb (struct android_app* app, int32_t cmd)
|
|
{
|
|
AndroidSalInstance::getInstance()->onAppCmd(app, cmd);
|
|
}
|
|
|
|
int32_t onInputEvent_cb (struct android_app* app, AInputEvent* event)
|
|
{
|
|
return AndroidSalInstance::getInstance()->onInputEvent(app, event);
|
|
}
|
|
}
|
|
|
|
AndroidSalInstance::AndroidSalInstance( SalYieldMutex *pMutex )
|
|
: SvpSalInstance( pMutex )
|
|
{
|
|
mpApp = lo_get_app();
|
|
fprintf (stderr, "created Android Sal Instance for app %p window %p\n",
|
|
mpApp,
|
|
mpApp ? mpApp->window : NULL);
|
|
if (mpApp)
|
|
{
|
|
pthread_mutex_lock (&mpApp->mutex);
|
|
mpApp->onAppCmd = onAppCmd_cb;
|
|
mpApp->onInputEvent = onInputEvent_cb;
|
|
if (mpApp->window != NULL)
|
|
onAppCmd_cb (mpApp, APP_CMD_INIT_WINDOW);
|
|
pthread_mutex_unlock (&mpApp->mutex);
|
|
}
|
|
}
|
|
|
|
AndroidSalInstance::~AndroidSalInstance()
|
|
{
|
|
fprintf (stderr, "destroyed Android Sal Instance\n");
|
|
}
|
|
|
|
void AndroidSalInstance::Wakeup()
|
|
{
|
|
fprintf (stderr, "Wakeup alooper\n");
|
|
if (mpApp && mpApp->looper)
|
|
ALooper_wake (mpApp->looper);
|
|
else
|
|
fprintf (stderr, "busted - no global looper\n");
|
|
}
|
|
|
|
void AndroidSalInstance::DoReleaseYield (int nTimeoutMS)
|
|
{
|
|
// release yield mutex
|
|
sal_uLong nAcquireCount = ReleaseYieldMutex();
|
|
|
|
fprintf (stderr, "DoReleaseYield #2 %d ms\n", nTimeoutMS);
|
|
void *outData = NULL;
|
|
int outFd = 0, outEvents = 0;
|
|
int nRet = ALooper_pollAll(nTimeoutMS, &outFd, &outEvents, &outData);
|
|
fprintf (stderr, "ret %d %d %d %p\n", nRet, outFd, outEvents, outData);
|
|
|
|
// acquire yield mutex again
|
|
AcquireYieldMutex(nAcquireCount);
|
|
|
|
// FIXME: this is more or less deranged: why can we not
|
|
// set a callback in the native app glue's ALooper_addFd ?
|
|
if (nRet == LOOPER_ID_MAIN)
|
|
mpApp->cmdPollSource.process(mpApp, &mpApp->cmdPollSource);
|
|
if (nRet == LOOPER_ID_INPUT)
|
|
mpApp->inputPollSource.process(mpApp, &mpApp->inputPollSource);
|
|
}
|
|
|
|
bool AndroidSalInstance::AnyInput( sal_uInt16 nType )
|
|
{
|
|
(void) nType;
|
|
// FIXME: ideally we should check the input queue to avoid being busy ...
|
|
fprintf (stderr, "FIXME: AnyInput returns true\n");
|
|
// mpApp->inputQueue ? ...
|
|
return true;
|
|
}
|
|
|
|
class AndroidSalSystem : public SvpSalSystem {
|
|
public:
|
|
AndroidSalSystem() : SvpSalSystem() {}
|
|
virtual ~AndroidSalSystem() {}
|
|
virtual int ShowNativeDialog( const rtl::OUString& rTitle,
|
|
const rtl::OUString& rMessage,
|
|
const std::list< rtl::OUString >& rButtons,
|
|
int nDefButton )
|
|
{
|
|
(void)rButtons; (void)nDefButton;
|
|
__android_log_print(ANDROID_LOG_INFO, "LibreOffice - dialog '%s': '%s'",
|
|
rtl::OUStringToOString(rTitle, RTL_TEXTENCODING_ASCII_US).getStr(),
|
|
rtl::OUStringToOString(rMessage, RTL_TEXTENCODING_ASCII_US).getStr());
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
SalSystem *AndroidSalInstance::CreateSalSystem()
|
|
{
|
|
return new AndroidSalSystem();
|
|
}
|
|
|
|
// All the interesting stuff is slaved from the AndroidSalInstance
|
|
void InitSalData() {}
|
|
void DeInitSalData() {}
|
|
void InitSalMain() {}
|
|
void DeInitSalMain() {}
|
|
|
|
void SalAbort( const rtl::OUString& rErrorText, bool bDumpCore )
|
|
{
|
|
rtl::OUString aError( rErrorText );
|
|
if( aError.isEmpty() )
|
|
aError = rtl::OUString::createFromAscii("Unknown application error");
|
|
::fprintf( stderr, "%s\n", rtl::OUStringToOString(rErrorText, osl_getThreadTextEncoding()).getStr() );
|
|
|
|
__android_log_print(ANDROID_LOG_INFO, "SalAbort: '%s'",
|
|
rtl::OUStringToOString(aError, RTL_TEXTENCODING_ASCII_US).getStr());
|
|
if( bDumpCore )
|
|
abort();
|
|
else
|
|
_exit(1);
|
|
}
|
|
|
|
const OUString& SalGetDesktopEnvironment()
|
|
{
|
|
static rtl::OUString aEnv( RTL_CONSTASCII_USTRINGPARAM( "android" ) );
|
|
return aEnv;
|
|
}
|
|
|
|
SalData::SalData() :
|
|
m_pInstance( 0 ),
|
|
m_pPlugin( 0 ),
|
|
m_pPIManager(0 )
|
|
{
|
|
}
|
|
|
|
SalData::~SalData()
|
|
{
|
|
}
|
|
|
|
// This is our main entry point:
|
|
SalInstance *CreateSalInstance()
|
|
{
|
|
AndroidSalInstance* pInstance = new AndroidSalInstance( new SalYieldMutex() );
|
|
new AndroidSalData( pInstance );
|
|
return pInstance;
|
|
}
|
|
|
|
void DestroySalInstance( SalInstance *pInst )
|
|
{
|
|
pInst->ReleaseYieldMutex();
|
|
delete pInst;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|