初始化

This commit is contained in:
xhm 2023-11-21 23:05:03 +08:00
commit 2455630dad
2252 changed files with 466529 additions and 0 deletions

63
.gitattributes vendored Normal file
View File

@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

358
.gitignore vendored Normal file
View File

@ -0,0 +1,358 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
App1/
packages/
CHM/
Private/
NESPlayer/
CPF_Demo/
ConsoleApp2/
CPFItemTemplate/
CPFProjectTemplate/
CPFWindowTemplate/
CPFProjectNetCore3AndNet4/
WindowsFormsApp1/
蓝图重制版/
CPF.Cef/
CPF.Vlc/
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true
**/wwwroot/lib/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
/.svn

View File

@ -0,0 +1,188 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{C7338F66-2EAF-4F0A-9306-53232DD20501}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{122416d6-6b49-4ee2-a1e8-b825f31c79fe}</TemplateGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>AndroidTest</RootNamespace>
<AssemblyName>AndroidTest</AssemblyName>
<FileAlignment>512</FileAlignment>
<Deterministic>True</Deterministic>
<AndroidApplication>True</AndroidApplication>
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
<AndroidResgenClass>Resource</AndroidResgenClass>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v9.0</TargetFrameworkVersion>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
<AndroidEnableSGenConcurrent>true</AndroidEnableSGenConcurrent>
<AndroidUseAapt2>true</AndroidUseAapt2>
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>True</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
<EmbedAssembliesIntoApk>false</EmbedAssembliesIntoApk>
<AotAssemblies>false</AotAssemblies>
<EnableLLVM>false</EnableLLVM>
<AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
<BundleAssemblies>false</BundleAssemblies>
<AndroidSupportedAbis />
<AndroidCreatePackagePerAbi>false</AndroidCreatePackagePerAbi>
<Debugger>Xamarin</Debugger>
<AndroidEnableMultiDex>false</AndroidEnableMultiDex>
<AotAssemblies>True</AotAssemblies>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugSymbols>True</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidManagedSymbols>true</AndroidManagedSymbols>
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<AotAssemblies>false</AotAssemblies>
<EnableLLVM>false</EnableLLVM>
<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
<BundleAssemblies>false</BundleAssemblies>
<AndroidEnableMultiDex>false</AndroidEnableMultiDex>
<AotAssemblies>True</AotAssemblies>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="Mono.Android" />
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors" />
</ItemGroup>
<ItemGroup>
<Compile Include="Class1.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="MainActivity.cs" />
<Compile Include="Resources\Resource.designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\AboutResources.txt" />
<None Include="Properties\AndroidManifest.xml" />
<None Include="Assets\AboutAssets.txt" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\values\ic_launcher_background.xml" />
<AndroidResource Include="Resources\values\strings.xml" />
</ItemGroup>
<ItemGroup>
<Folder Include="Resources\drawable\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.2.0.5" />
<PackageReference Include="Xamarin.Essentials" Version="1.6.1" />
<PackageReference Include="Xamarin.AndroidX.Lifecycle.LiveData" Version="2.2.0.3" />
<PackageReference Include="Xamarin.AndroidX.Legacy.Support.Core.UI" Version="1.0.0.5" />
<PackageReference Include="Xamarin.AndroidX.Browser" Version="1.3.0.3-alpha01" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj">
<Project>{d1df2cdd-e6b1-4844-b3de-0f414091e972}</Project>
<Name>ClassLibrary1</Name>
</ProjectReference>
<ProjectReference Include="..\CPF.Android\CPF.Android.csproj">
<Project>{6255e6d7-7ee3-4cb9-af6c-f75b52341294}</Project>
<Name>CPF.Android</Name>
</ProjectReference>
<ProjectReference Include="..\CPF.Skia\CPF.Skia.csproj">
<Project>{e6495f31-7937-4a69-a915-834bfa1a031f}</Project>
<Name>CPF.Skia</Name>
</ProjectReference>
<ProjectReference Include="..\CPF\CPF.csproj">
<Project>{5c1e41fd-fd6b-4107-b429-1db7bd5c37d5}</Project>
<Name>CPF</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\values\styles.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\values\colors.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher_round.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher_foreground.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher_round.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher_foreground.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher_round.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher_foreground.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher_round.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher_foreground.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher_round.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_foreground.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_round.png" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,19 @@
Any raw assets you want to be deployed with your application can be placed in
this directory (and child directories) and given a Build Action of "AndroidAsset".
These files will be deployed with your package and will be accessible using Android's
AssetManager, like this:
public class ReadAsset : Activity
{
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
InputStream input = Assets.Open ("my_asset.txt");
}
}
Additionally, some Android functions will automatically load asset files:
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");

21
AndroidTest/Class1.cs Normal file
View File

@ -0,0 +1,21 @@
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AndroidTest
{
class Class1:CPF.Controls.Control
{
protected override void InitializeComponent()
{
base.InitializeComponent();
}
}
}

View File

@ -0,0 +1,28 @@
using Android.App;
using Android.OS;
//using Android.Runtime;
//using AndroidX.AppCompat.App;
using CPF.Platform;
namespace AndroidTest
{
[Activity(Label = "@string/app_name", MainLauncher = true)]
public class MainActivity : CPF.Android.CpfActivity
{
static MainActivity()
{
CPF.Platform.Application.Initialize((OperatingSystemType.Android, new CPF.Android.AndroidPlatform(), new CPF.Skia.SkiaDrawingFactory { ClearType = true, UseGPU = true }));
}
protected override void OnCreate(Bundle savedInstanceState)
{
//Window.SetSoftInputMode(SoftInput.AdjustResize);
base.OnCreate(savedInstanceState);
ClassLibrary1.Class1.CreateNativeControl = () => new global::Android.Widget.Button(this) { Text = "原生控件" };
SetContentView(new CPF.Android.CpfView(new ClassLibrary1.Class1()));
//this.Window.AddContentView();
}
}
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.androidtest" android:installLocation="auto">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28" />
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
</manifest>

View File

@ -0,0 +1,26 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Android.App;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("AndroidTest")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("AndroidTest")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,44 @@
Images, layout descriptions, binary blobs and string dictionaries can be included
in your application as resource files. Various Android APIs are designed to
operate on the resource IDs instead of dealing with images, strings or binary blobs
directly.
For example, a sample Android app that contains a user interface layout (main.xml),
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
would keep its resources in the "Resources" directory of the application:
Resources/
drawable/
icon.png
layout/
main.xml
values/
strings.xml
In order to get the build system to recognize Android resources, set the build action to
"AndroidResource". The native Android APIs do not operate directly with filenames, but
instead operate on resource IDs. When you compile an Android application that uses resources,
the build system will package the resources for distribution and generate a class called "R"
(this is an Android convention) that contains the tokens for each one of the resources
included. For example, for the above Resources layout, this is what the R class would expose:
public class R {
public class drawable {
public const int icon = 0x123;
}
public class layout {
public const int main = 0x456;
}
public class strings {
public const int first_string = 0xabc;
public const int second_string = 0xbcd;
}
}
You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main
to reference the layout/main.xml file, or R.strings.first_string to reference the first
string in the dictionary file values/strings.xml.

6392
AndroidTest/Resources/Resource.designer.cs generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="100dp"
android:layout_height="50dp" android:text="测试"/>
</RelativeLayout>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 958 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#2c3e50</color>
<color name="colorPrimaryDark">#1B3147</color>
<color name="colorAccent">#3498db</color>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#2C3E50</color>
</resources>

View File

@ -0,0 +1,4 @@
<resources>
<string name="app_name">AndroidTest</string>
<string name="action_settings">Settings</string>
</resources>

View File

@ -0,0 +1,11 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="SkiaSharp" version="1.68.3" targetFramework="monoandroid90" />
</packages>

View File

@ -0,0 +1,671 @@
using Android.Content;
using CPF.Controls;
using CPF.Input;
using CPF.Platform;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Xamarin.Essentials;
using System.Linq;
using Android.OS;
namespace CPF.Android
{
public class AndroidPlatform : RuntimePlatform
{
internal PixelPoint mousePosition;
public override PixelPoint MousePosition => mousePosition;
public override TimeSpan DoubleClickTime => TimeSpan.FromSeconds(0.5);
public override IPopupImpl CreatePopup()
{
return new PopupImpl();
}
public override IWindowImpl CreateWindow()
{
return new WindowImpl();
}
public override DragDropEffects DoDragDrop(DragDropEffects allowedEffects, params (DataFormat, object)[] data)
{
throw new NotImplementedException();
}
public override IReadOnlyList<Screen> GetAllScreen()
{
var display = CpfActivity.CurrentActivity.WindowManager.DefaultDisplay;
global::Android.Graphics.Rect rect = new global::Android.Graphics.Rect();
display.GetRectSize(rect);
global::Android.Graphics.Point point1 = new global::Android.Graphics.Point();
display.GetSize(point1);
var screen = new Screen(new Drawing.Rect(0, 0, point1.X, point1.Y), new Drawing.Rect(rect.Left, rect.Top, rect.Right, rect.Bottom), true);
return new Screen[] { screen };
}
public override IClipboard GetClipboard()
{
return new ClipboardImpl();
}
public override object GetCursor(Cursors cursorType)
{
return cursorType;
}
public override SynchronizationContext GetSynchronizationContext()
{
return new AndroidSynchronizationContext();
}
static Dictionary<KeyGesture, PlatformHotkey> keyValuePairs = new Dictionary<KeyGesture, PlatformHotkey>() {
{ new KeyGesture(Keys.C,InputModifiers.Control),PlatformHotkey.Copy},
{ new KeyGesture(Keys.X,InputModifiers.Control),PlatformHotkey.Cut},
{ new KeyGesture(Keys.V,InputModifiers.Control),PlatformHotkey.Paste},
{ new KeyGesture(Keys.Y,InputModifiers.Control),PlatformHotkey.Redo},
{ new KeyGesture(Keys.A,InputModifiers.Control),PlatformHotkey.SelectAll},
{ new KeyGesture(Keys.Z,InputModifiers.Control),PlatformHotkey.Undo},
};
public override PlatformHotkey Hotkey(KeyGesture keyGesture)
{
keyValuePairs.TryGetValue(keyGesture, out PlatformHotkey platformHotkey);
return platformHotkey;
}
public override void Run()
{
}
public override Task<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent)
{
//var activity = CpfActivity.CurrentActivity as CpfActivity;
//if (activity != null && activity.Main != null)
//{
// //activity.fileName = new TaskCompletionSource<string[]>();
// //FileSaveFragment fileSaveFragment = FileSaveFragment.newInstance("*/*", 500, Resource.String.btn_ok, Resource.String.btn_cancel, Resource.String.tag_title_SaveFile, Resource.String.tag_save_hint, Resource.Drawable.filedialog_root_l, Resource.Drawable.filedialog_folder_l, Resource.Drawable.filedialog_folder_up_l, Resource.Drawable.filedialog_xlsfile_l);
// //fileSaveFragment.Show(activity.FragmentManager, fileSaveFragment.Tag);
// //return activity.fileName.Task;
// var f = new OpenFileDialogView();
// activity.Main.Root.Children.Add(f);
//}
//return Task.Run(() => new string[0]);
return Task.Run(() =>
{
if (dialog is OpenFileDialog open)
{
var p = new PickOptions { PickerTitle = dialog.Title };
if (open.Filters != null && open.Filters.Count > 0)
{
HashSet<string> ex = new HashSet<string>();
foreach (var item in open.Filters)
{
if (!string.IsNullOrWhiteSpace(item.Extensions))
{
var es = item.Extensions.Split(',');
foreach (var e in es)
{
if (e == null)
{
continue;
}
if (MIME_Map.TryGetValue(e, out var ee))
{
ex.Add(ee);
}
}
}
}
p.FileTypes = new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.Android, ex }
});
}
if (!open.AllowMultiple)
{
Task<FileResult> fileTask = null;
CPF.Threading.Dispatcher.MainThread.Invoke(() =>
{
fileTask = FilePicker.PickAsync(p);
});
var file = fileTask.Result;
if (file != null)
{
return new string[] { file.FullPath };
}
}
else
{
Task<IEnumerable<FileResult>> fileTask = null;
CPF.Threading.Dispatcher.MainThread.Invoke(() =>
{
fileTask = FilePicker.PickMultipleAsync(p);
});
var files = fileTask.Result;
if (files != null)
{
return files.Select(a => a.FullPath).ToArray();
}
}
}
else if (dialog is SaveFileDialog save)
{
throw new NotImplementedException();
}
return new string[0];
});
}
public override Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent)
{
throw new NotImplementedException();
}
public override INativeImpl CreateNative()
{
return new NativeImpl();
}
public override INotifyIconImpl CreateNotifyIcon()
{
return null;
}
public override void Run(CancellationToken cancellation)
{
throw new NotSupportedException("安卓不能支持主线程的消息循环控制");
//var queue = Looper.MyLooper().Queue;
//var next = queue.Class.GetDeclaredMethod("next");
//next.Accessible = true;
//while (!cancellation.IsCancellationRequested)
//{
// var message = (Message)next.Invoke(queue);
// if (message == null)
// {
// break;
// }
// try
// {
// message.Target.DispatchMessage(message);
// }
// catch (Exception e)
// {
// System.Diagnostics.Debug.WriteLine(e);
// }
// //Binder.ClearCallingIdentity();
// //var recycleUnchecked = message.Class.GetDeclaredMethod("recycleUnchecked");
// //recycleUnchecked.Accessible = true;
// //recycleUnchecked.Invoke(message);
// //message.Recycle();
//}
//cancellation.Register(() =>
//{
// Looper.MyLooper().Quit();
//});
//cancellation.ThrowIfCancellationRequested();
cancellation.Register(() =>
{
throw new Java.Lang.RuntimeException();
});
try
{
Looper.Loop();
}
catch (Exception e)
{
}
}
static Dictionary<string, string> MIME_Map = new Dictionary<string, string>
{
//{后缀名MIME类型}
{"pbm", "image/x-portable-bitmap"},
{"pcx", "image/x-pcx"},
{"nbmp", "image/nbmp"},
{"pda", "image/x-pda"},
{"pgm", "image/x-portable-graymap"},
{"pict", "image/x-pict"},
{"png", "image/png"},
{"pnm", "image/x-portable-anymap"},
{"pnz", "image/png"},
{"ppm", "image/x-portable-pixmap"},
{"nokia-op-logo", "image/vnd.nok-oplogo-color"},
{"qti", "image/x-quicktime"},
{"qtif", "image/x-quicktime"},
{"ras", "image/x-cmu-raster"},
{"rf", "image/vnd.rn-realflash"},
{"rp", "image/vnd.rn-realpix"},
{"rgb", "image/x-rgb"},
{"si9", "image/vnd.lgtwap.sis"},
{"si7", "image/vnd.stiwap.sis"},
{"svf", "image/vnd"},
{"svg", "image/svg-xml"},
{"svh", "image/svh"},
{"si6", "image/si6"},
{"tif", "image/tiff"},
{"tiff", "image/tiff"},
{"toy", "image/toy"},
{"wbmp", "image/vnd.wap.wbmp"},
{"wi", "image/wavelet"},
{"wpng", "image/x-up-wpng"},
{"xbm", "image/x-xbitmap"},
{"xpm", "image/x-xpixmap"},
{"xwd", "image/x-xwindowdump"},
{"fh4", "image/x-freehand"},
{"fh5", "image/x-freehand"},
{"fhc", "image/x-freehand"},
{"fif", "image/fif"},
{"bmp", "image/bmp"},
{"cal", "image/x-cals"},
{"cod", "image/cis-cod"},
{"fpx", "image/x-fpx"},
{"dcx", "image/x-dcx"},
{"eri", "image/x-eri"},
{"gif", "image/gif"},
{"ief", "image/ief"},
{"ifm", "image/gif"},
{"ifs", "image/ifs"},
{"j2k", "image/j2k"},
{"jpe", "image/jpeg"},
{"jpeg", "image/jpeg"},
{"jpg", "image/jpeg"},
{"jpz", "image/jpeg"},
{"mil", "image/x-cals"},
{"3gp", "video/3gpp"},
{"asf", "video/x-ms-asf"},
{"asx", "video/x-ms-asf"},
{"avi", "video/x-msvideo"},
{"fvi", "video/isivideo"},
{"lsf", "video/x-ms-asf"},
{"lsx", "video/x-ms-asf"},
{"m4u", "video/vnd.mpegurl"},
{"m4v", "video/x-m4v"},
{"pvx", "video/x-pv-pvx"},
{"qt", "video/quicktime"},
{"rv", "video/vnd.rn-realvideo"},
{"viv", "video/vivo"},
{"vivo", "video/vivo"},
{"vdo", "video/vdo"},
{"wm", "video/x-ms-wm"},
{"wmx", "video/x-ms-wmx"},
{"wv", "video/wavelet"},
{"wvx", "video/x-ms-wvx"},
{"mov", "video/quicktime"},
{"movie", "video/x-sgi-movie"},
{"mp4", "video/mp4"},
{"mng", "video/x-mng"},
{"mpe", "video/mpeg"},
{"mpeg", "video/mpeg"},
{"mpg", "video/mpeg"},
{"mpg4", "video/mp4"},
{"aif", "audio/x-aiff"},
{"aifc", "audio/x-aiff"},
{"aiff", "audio/x-aiff"},
{"als", "audio/X-Alpha5"},
{"au", "audio/basic"},
{"es", "audio/echospeech"},
{"esl", "audio/echospeech"},
{"awb", "audio/amr-wb"},
{"imy", "audio/melody"},
{"it", "audio/x-mod"},
{"itz", "audio/x-mod"},
{"tsi", "audio/tsplayer"},
{"ult", "audio/x-mod"},
{"vib", "audio/vib"},
{"vox", "audio/voxware"},
{"vqe", "audio/x-twinvq-plugin"},
{"vqf", "audio/x-twinvq"},
{"vql", "audio/x-twinvq"},
{"wav", "audio/x-wav"},
{"wax", "audio/x-ms-wax"},
{"wmv", "audio/x-ms-wmv"},
{"wma", "audio/x-ms-wma"},
{"xmz", "audio/x-mod"},
{"m15", "audio/x-mod"},
{"m3u", "audio/x-mpegurl"},
{"m3url", "audio/x-mpegurl"},
{"m4a", "audio/mp4a-latm"},
{"m4b", "audio/mp4a-latm"},
{"m4p", "audio/mp4a-latm"},
{"ma1", "audio/ma1"},
{"ma2", "audio/ma2"},
{"ma3", "audio/ma3"},
{"ma5", "audio/ma5"},
{"mdz", "audio/x-mod"},
{"mid", "audio/midi"},
{"midi", "audio/midi"},
{"mio", "audio/x-mio"},
{"mod", "audio/x-mod"},
{"mp2", "audio/x-mpeg"},
{"mp3", "audio/x-mpeg"},
{"mpga", "audio/mpeg"},
{"ogg", "audio/ogg"},
{"nsnd", "audio/nsnd"},
{"pae", "audio/x-epac"},
{"pac", "audio/x-pac"},
{"qcp", "audio/vnd.qcelp"},
{"ra", "audio/x-pn-realaudio"},
{"ram", "audio/x-pn-realaudio"},
{"rm", "audio/x-pn-realaudio"},
{"rmf", "audio/x-rmf"},
{"rmm", "audio/x-pn-realaudio"},
{"rmvb", "audio/x-pn-realaudio"},
{"rpm", "audio/x-pn-realaudio-plugin"},
{"s3m", "audio/x-mod"},
{"s3z", "audio/x-mod"},
{"stm", "audio/x-mod"},
{"smz", "audio/x-smd"},
{"snd", "audio/basic"},
{"smd", "audio/x-smd"},
{"xm", "audio/x-mod"},
{"c", "text/plain"},
{"asc", "text/plain"},
{"conf", "text/plain"},
{"cpp", "text/plain"},
{"css", "text/css"},
{"dhtml", "text/html"},
{"etx", "text/x-setext"},
{"h", "text/plain"},
{"hdm", "text/x-hdml"},
{"hdml", "text/x-hdml"},
{"htm", "text/html"},
{"html", "text/html"},
{"hts", "text/html"},
{"jad", "text/vnd.sun.j2me.app-descriptor"},
{"java", "text/plain"},
{"log", "text/plain"},
{"mel", "text/x-vmel"},
{"mrl", "text/x-mrml"},
{"prop", "text/plain"},
{"r3t", "text/vnd.rn-realtext3d"},
{"sgm", "text/x-sgml"},
{"rc", "text/plain"},
{"rtx", "text/richtext"},
{"rt", "text/vnd.rn-realtext"},
{"sgml", "text/x-sgml"},
{"spc", "text/x-speech"},
{"txt", "text/plain"},
{"tsv", "text/tab-separated-values"},
{"talk", "text/x-speech"},
{"vcf", "text/x-vcard"},
{"wml", "text/vnd.wap.wml"},
{"wmls", "text/vnd.wap.wmlscript"},
{"wmlscript", "text/vnd.wap.wmlscript"},
{"ws", "text/vnd.wap.wmlscript"},
{"xml", "text/xml"},
{"xsit", "text/xml"},
{"xsl", "text/xml"},
{"xul", "text/xul"},
{"apk", "application/vnd.android.package-archive"},
{"aab", "application/x-authoware-bin"},
{"aam", "application/x-authoware-map"},
{"aas", "application/x-authoware-seg"},
{"ai", "application/postscript"},
{"amc", "application/x-mpeg"},
{"ani", "application/octet-stream"},
{"asd", "application/astound"},
{"asn", "application/astound"},
{"asp", "application/x-asap"},
{"avb", "application/octet-stream"},
{"bcpio", "application/x-bcpio"},
{"bin", "application/octet-stream"},
{"bld", "application/bld"},
{"bld2", "application/bld2"},
{"bpk", "application/octet-stream"},
{"bz2", "application/x-bzip2"},
{"ccn", "application/x-cnc"},
{"cco", "application/x-cocoa"},
{"cdf", "application/x-netcdf"},
{"chat", "application/x-chat"},
{"class", "application/octet-stream"},
{"clp", "application/x-msclip"},
{"cmx", "application/x-cmx"},
{"co", "application/x-cult3d-object"},
{"cpio", "application/x-cpio"},
{"cpt", "application/mac-compactpro"},
{"crd", "application/x-mscardfile"},
{"csh", "application/x-csh"},
{"cur", "application/octet-stream"},
{"dcr", "application/x-director"},
{"dir", "application/x-director"},
{"dll", "application/octet-stream"},
{"dmg", "application/octet-stream"},
{"dms", "application/octet-stream"},
{"doc", "application/msword"},
{"docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
{"dot", "application/x-dot"},
{"dvi", "application/x-dvi"},
{"dwg", "application/x-autocad"},
{"dxf", "application/x-autocad"},
{"dxr", "application/x-director"},
{"ebk", "application/x-expandedbook"},
{"eps", "application/postscript"},
{"evy", "application/x-envoy"},
{"exe", "application/octet-stream"},
{"etc", "application/x-earthtime"},
{"fm", "application/x-maker"},
{"gps", "application/x-gps"},
{"gtar", "application/x-gtar"},
{"gz", "application/x-gzip"},
{"gca", "application/x-gca-compressed"},
{"hdf", "application/x-hdf"},
{"hlp", "application/winhlp"},
{"hqx", "application/mac-binhex40"},
{"ico", "application/octet-stream"},
{"ins", "application/x-NET-Install"},
{"ips", "application/x-ipscript"},
{"ipx", "application/x-ipix"},
{"jam", "application/x-jam"},
{"jar", "application/java-archive"},
{"jnlp", "application/x-java-jnlp-file"},
{"latex", "application/x-latex"},
{"lcc", "application/fastman"},
{"lcl", "application/x-digitalloca"},
{"lcr", "application/x-digitalloca"},
{"lgh", "application/lgh"},
{"lha", "application/octet-stream"},
{"js", "application/x-javascript"},
{"jwc", "application/jwc"},
{"kjx", "application/x-kjx"},
{"lzh", "application/x-lzh"},
{"m13", "application/x-msmediaview"},
{"m14", "application/x-msmediaview"},
{"man", "application/x-troff-man"},
{"mbd", "application/mbedlet"},
{"mct", "application/x-mascot"},
{"mdb", "application/x-msaccess"},
{"me", "application/x-troff-me"},
{"mi", "application/x-mif"},
{"mif", "application/x-mif"},
{"mmf", "application/x-skt-lbs"},
{"mny", "application/x-msmoney"},
{"moc", "application/x-mocha"},
{"mocha", "application/x-mocha"},
{"mpn", "application/vnd.mophun.application"},
{"mpc", "application/vnd.mpohun.certificate"},
{"mof", "application/x-yumekara"},
{"mpp", "application/vnd.ms-project"},
{"mps", "application/x-mapserver"},
{"mrm", "application/x-mrm"},
{"ms", "application/x-troff-ms"},
{"msg", "application/vnd.ms-outlook"},
{"mts", "application/metastream"},
{"mtx", "application/metastream"},
{"mtz", "application/metastream"},
{"mzv", "application/metastream"},
{"nar", "application/zip"},
{"nc", "application/x-netcdf"},
{"ndwn", "application/ndwn"},
{"nif", "application/x-nif"},
{"nmz", "application/x-scream"},
{"npx", "application/x-netfpx"},
{"nva", "application/x-neva1"},
{"oda", "application/oda"},
{"oom", "application/x-AtlasMate-Plugin"},
{"pan", "application/x-pan"},
{"pdf", "application/pdf"},
{"pfr", "application/font-tdpfr"},
{"pm", "application/x-perl"},
{"pmd", "application/x-pmd"},
{"pot", "application/vnd.ms-powerpoint"},
{"pps", "application/vnd.ms-powerpoint"},
{"ppt", "application/vnd.ms-powerpoint"},
{"pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
{"pqf", "application/x-cprplayer"},
{"pqi", "application/cprplayer"},
{"proxy", "application/x-ns-proxy-autoconfig"},
{"ps", "application/postscript"},
{"ptlk", "application/listenup"},
{"pub", "application/x-mspublisher"},
{"prc", "application/x-prc"},
{"rar", "application/x-rar-compressed"},
{"rdf", "application/rdf+xml"},
{"rlf", "application/x-richlink"},
{"rnx", "application/vnd.rn-realplayer"},
{"roff", "application/x-troff"},
{"rtf", "application/rtf"},
{"rtg", "application/metastream"},
{"rwc", "application/x-rogerwilco"},
{"sca", "application/x-supercard"},
{"scd", "application/x-msschedule"},
{"sdf", "application/e-score"},
{"sea", "application/x-stuffit"},
{"sh", "application/x-sh"},
{"shw", "application/presentations"},
{"shar", "application/x-shar"},
{"sis", "application/vnd.symbian.install"},
{"sit", "application/x-stuffit"},
{"skd", "application/x-Koan"},
{"skm", "application/x-Koan"},
{"skp", "application/x-Koan"},
{"skt", "application/x-Koan"},
{"slc", "application/x-salsa"},
{"smi", "application/smil"},
{"smil", "application/smil"},
{"smp", "application/studiom"},
{"spl", "application/futuresplash"},
{"spr", "application/x-sprite"},
{"sprite", "application/x-sprite"},
{"spt", "application/x-spt"},
{"src", "application/x-wais-source"},
{"stk", "application/hyperstudio"},
{"sv4cpio", "application/x-sv4cpio"},
{"sv4crc", "application/x-sv4crc"},
{"swf", "application/x-shockwave-flash"},
{"swfl", "application/x-shockwave-flash"},
{"t", "application/x-troff"},
{"tad", "application/octet-stream"},
{"tar", "application/x-tar"},
{"taz", "application/x-tar"},
{"tbp", "application/x-timbuktu"},
{"tbt", "application/x-timbuktu"},
{"tcl", "application/x-tcl"},
{"tex", "application/x-tex"},
{"texi", "application/x-texinfo"},
{"texinfo", "application/x-texinfo"},
{"tgz", "application/x-tar"},
{"thm", "application/vnd.eri.thm"},
{"tki", "application/x-tkined"},
{"tkined", "application/x-tkined"},
{"toc", "application/toc"},
{"tr", "application/x-troff"},
{"trm", "application/x-msterminal"},
{"tsp", "application/dsptype"},
{"ttf", "application/octet-stream"},
{"ttz", "application/t-time"},
{"ustar", "application/x-ustar"},
{"uu", "application/x-uuencode"},
{"uue", "application/x-uuencode"},
{"vcd", "application/x-cdlink"},
{"vmd", "application/vocaltec-media-desc"},
{"vmf", "application/vocaltec-media-file"},
{"vmi", "application/x-dreamcast-vms-info"},
{"vms", "application/x-dreamcast-vms"},
{"wis", "application/x-InstallShield"},
{"wmd", "application/x-ms-wmd"},
{"wmf", "application/x-msmetafile"},
{"wmlc", "application/vnd.wap.wmlc"},
{"wmlsc", "application/vnd.wap.wmlscriptc"},
{"wps", "application/vnd.ms-works"},
{"wmz", "application/x-ms-wmz"},
{"wri", "application/x-mswrite"},
{"web", "application/vnd.xara"},
{"wsc", "application/vnd.wap.wmlscriptc"},
{"wxl", "application/x-wxl"},
{"x-gzip", "application/x-gzip"},
{"xar", "application/vnd.xara"},
{"xdm", "application/x-xdma"},
{"xdma", "application/x-xdma"},
{"xdw", "application/vnd.fujixerox.docuworks"},
{"xht", "application/xhtml+xml"},
{"xhtm", "application/xhtml+xml"},
{"xhtml", "application/xhtml+xml"},
{"xla", "application/vnd.ms-excel"},
{"xlc", "application/vnd.ms-excel"},
{"xll", "application/x-excel"},
{"xlm", "application/vnd.ms-excel"},
{"xls", "application/vnd.ms-excel"},
{"xlt", "application/vnd.ms-excel"},
{"xlw", "application/vnd.ms-excel"},
{"xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
{"xpi", "application/x-xpinstall"},
{"yz1", "application/x-yz1"},
{"z", "application/x-compress"},
{"zac", "application/x-zaurus-zac"},
{"zip", "application/zip"},
{"gau", "chemical/x-gaussian-input"},
{"csm", "chemical/x-csml"},
{"csml", "chemical/x-csml"},
{"emb", "chemical/x-embl-dl-nucleotide"},
{"embl", "chemical/x-embl-dl-nucleotide"},
{"mol", "chemical/x-mdl-molfile"},
{"pdb", "chemical/x-pdb"},
{"xyz", "chemical/x-pdb"},
{"mop", "chemical/x-mopac-input"},
{"dcm", "x-lml/x-evm"},
{"evm", "x-lml/x-evm"},
{"gdb", "x-lml/x-gdb"},
{"lak", "x-lml/x-lak"},
{"lml", "x-lml/x-lml"},
{"lmlpack", "x-lml/x-lmlpack"},
{"ndb", "x-lml/x-ndb"},
{"rte", "x-lml/x-gps"},
{"wpt", "x-lml/x-gps"},
{"trk", "x-lml/x-gps"},
{"svr", "x-world/x-svr"},
{"ivr", "i-world/i-vrml"},
{"vre", "x-world/x-vream"},
{"vrml", "x-world/x-vrml"},
{"vrt", "x-world/x-vrt"},
{"vrw", "x-world/x-vream"},
{"vts", "workbook/formulaone"},
{"wrl", "x-world/x-vrml"},
{"wrz", "x-world/x-vrml"},
{"dwf", "drawing/x-dwf"},
{"ice", "x-conference/x-cooltalk"},
{"map", "magnus-internal/imagemap"},
{"shtml", "magnus-internal/parsed-html"},
{"cgi", "magnus-internal/cgi"},
{"", "*/*"}
};
}
}

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
namespace CPF.Android
{
class AndroidSynchronizationContext : SynchronizationContext
{
private Handler _handler;
public AndroidSynchronizationContext()
{
_handler = new Handler(Application.Context.MainLooper);
}
public override void Post(SendOrPostCallback d, object state)
{
_handler.Post(() =>
{
d(state);
});
}
public override void Send(SendOrPostCallback d, object state)
{
if (CPF.Threading.Dispatcher.MainThread.CheckAccess())
{
d(state);
}
else
{
ManualResetEvent manual = new ManualResetEvent(false);
_handler.Post(() =>
{
d(state);
manual.Set();
});
manual.WaitOne();
}
}
}
}

465
CPF.Android/AndroidView.cs Normal file
View File

@ -0,0 +1,465 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Views.InputMethods;
using Android.Widget;
using CPF.Drawing;
using CPF.Input;
using CPF.OpenGL;
using CPF.Platform;
using Javax.Microedition.Khronos.Egl;
using GLES20 = global::Android.Opengl.GLES20;
namespace CPF.Android
{
internal class AndroidView : SurfaceView, ISurfaceView, ISurfaceHolderCallback
{
public AndroidView(Context activity, UIElement content, CpfView cpfView) : base(activity)
{
if (CPF.Platform.Application.GetDrawingFactory().UseGPU)
{
eglContext = new EglContext(this);
Holder.AddCallback(this);
//SetLayerType(LayerType.Hardware, null);
}
CpfView = cpfView;
this.Holder.SetFormat(Format.Rgba8888);
//SetOnTouchListener(this);
//SetOnKeyListener(this);
//this.activity = activity as Activity;
//if (content != null)
//{
// root = new InnerView(this);
// root.Background = CPF.Drawing.Color.White;
// root.Children.Add(content);
// root.Visibility = CPF.Visibility.Visible;
// root.CanActivate = true;
// root.LayoutUpdated += Root_LayoutUpdated;
// scale = root.LayoutScaling;
//}
//this.FocusableInTouchMode = true;
//softKeyboardListner = new SoftKeyboardListner(this);
//ViewTreeObserver.AddOnGlobalLayoutListener(softKeyboardListner);
generalView = new GeneralView(this, content);
cpfView.AddView(this);
generalView.Create();
}
EglContext eglContext;
GeneralView generalView;
public CpfView CpfView { get; }
public GeneralView GeneralView { get { return generalView; } }
//internal Activity activity;
//CPF.Controls.View root;
//PixelSize oldSize;
public CPF.Controls.View Root
{
get { return generalView.Root; }
}
protected override int[] OnCreateDrawableState(int extraSpace)
{
//Invalidate();
generalView.OnCreateDrawableState(extraSpace);
return base.OnCreateDrawableState(extraSpace);
}
//bool _invalidateQueued;
public override void Invalidate()
{
//if (_invalidateQueued)
//{
// return;
//}
//_invalidateQueued = true;
//Threading.Dispatcher.MainThread.BeginInvoke(() =>
//{
// _invalidateQueued = false;
// if (CpfView.Handle != default)
// {
// OnPaint();
// }
//});
generalView.Invalidate();
}
public override void Invalidate(global::Android.Graphics.Rect dirty)
{
Invalidate();
}
public override void Invalidate(int l, int t, int r, int b)
{
Invalidate();
}
//float scale;
protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
{
//base.OnLayout(changed, left, top, right, bottom);
//var size = new PixelSize(right, bottom);
//var sc = LayoutScaling;
//if (LayoutScaling != scale)
//{
// scale = sc;
// ScalingChanged();
//}
//if (size != oldSize)
//{
// oldSize = size;
// Resized(new Size(size.Width / LayoutScaling, size.Height / LayoutScaling));
//}
generalView.OnLayout(changed, left, top, right, bottom);
}
PixelSize pixelSize;
public virtual void OnPaint()
{
//System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
//stopwatch.Start();
var root = generalView.Root;
if (eglContext != null)
{
var _window = ANativeWindow_fromSurface(JNIEnv.Handle, Holder.Surface.Handle);
if (_window == IntPtr.Zero)
return;//获取window操作可以解决下拉框图像错位问题
ANativeWindow_release(_window);
root.LayoutManager.ExecuteLayoutPass();
if (eglContext.EglSurface != null)
{
eglContext.MakeCurrent();
//var ir = generalView.invalidateRect * RenderScaling;
//GLES20.GlViewport(ir.X,ir.Y,ir.Width,ir.Height);
eglContext.GetFramebufferInfo(out var fb, out var sam, out var ste);
using (DrawingContext dc = DrawingContext.FromRenderTarget(new OpenGlRenderTarget(eglContext, pixelSize.Width, pixelSize.Height, fb, sam, ste)))
{
if (root.LayoutManager.VisibleUIElements != null)
{
root.RenderView(dc, new Drawing.Rect(0, 0, pixelSize.Width, pixelSize.Height));
}
}
eglContext.SwapBuffers();
}
}
else
{
var surface = this.Holder.Surface;
var _window = ANativeWindow_fromSurface(JNIEnv.Handle, surface.Handle);
if (_window == IntPtr.Zero)
return;
//throw new Exception("Unable to obtain ANativeWindow");
ANativeWindow_Buffer buffer;
var rc = new ARect()
{
right = ANativeWindow_getWidth(_window),
bottom = ANativeWindow_getHeight(_window)
};
var size = new PixelSize(rc.right, rc.bottom);
if (size.Width != Width || size.Height != Height)
{//有时候渲染尺寸和控件尺寸不一样
//surface.SetSize(size.Width, size.Height);
//Holder.SetSizeFromLayout();
Holder.SetFixedSize(size.Width, size.Height);
}
rc = new ARect()
{
right = ANativeWindow_getWidth(_window),
bottom = ANativeWindow_getHeight(_window)
};
size = new PixelSize(rc.right, rc.bottom);
ANativeWindow_lock(_window, out buffer, ref rc);
root.LayoutManager.ExecuteLayoutPass();
var Format = buffer.format == AndroidPixelFormat.WINDOW_FORMAT_RGB_565
? CPF.Drawing.PixelFormat.Rgb565 : CPF.Drawing.PixelFormat.Rgba;
var RowBytes = buffer.stride * (Format == CPF.Drawing.PixelFormat.Rgb565 ? 2 : 4);
var Address = buffer.bits;
using (Drawing.Bitmap bmp = new Drawing.Bitmap(size.Width, size.Height, RowBytes, Format, Address))
{
using (DrawingContext dc = DrawingContext.FromBitmap(bmp))
{
if (root.LayoutManager.VisibleUIElements != null)
{
//generalView.invalidateRect * RenderScaling
root.RenderView(dc, new Drawing.Rect(0,0,size.Width,size.Height));
}
}
}
ANativeWindow_unlockAndPost(_window);
ANativeWindow_release(_window);
}
//System.Diagnostics.Debug.WriteLine(stopwatch.ElapsedMilliseconds);
}
public override IInputConnection OnCreateInputConnection(EditorInfo outAttrs)
{
return generalView.OnCreateInputConnection(outAttrs);
}
public bool OnKey(View v, [GeneratedEnum] Keycode keyCode, KeyEvent e)
{
generalView.OnKey(v, keyCode, e);
return false;
}
public bool OnTouch(View v, MotionEvent e)
{
return generalView.OnTouch(v, e);
}
//public override bool DispatchKeyEvent(KeyEvent e)
//{
// //var m = new WindowManagerLayoutParams(100, 100, 100, 100, WindowManagerTypes.Phone | WindowManagerTypes.ApplicationOverlay, WindowManagerFlags.NotFocusable, global::Android.Graphics.Format.Rgbx8888);
// ////m.Gravity = GravityFlags.Left | GravityFlags.Top;
// //activity.WindowManager.AddView(new TestView(activity) { Background = new global::Android.Graphics.Drawables.ColorDrawable(global::Android.Graphics.Color.Argb(100, 255, 0, 0)) }, m);
//}
protected override void DispatchSetActivated(bool activated)
{
base.DispatchSetActivated(activated);
//if (activated)
//{
// (this as IViewImpl).Activated();
//}
//else
//{
// Deactivated();
//}
generalView.DispatchSetActivated(activated);
}
[DllImport("android")]
internal static extern IntPtr ANativeWindow_fromSurface(IntPtr jniEnv, IntPtr handle);
[DllImport("android")]
internal static extern int ANativeWindow_getWidth(IntPtr window);
[DllImport("android")]
internal static extern int ANativeWindow_getHeight(IntPtr window);
[DllImport("android")]
internal static extern void ANativeWindow_release(IntPtr window);
[DllImport("android")]
internal static extern void ANativeWindow_unlockAndPost(IntPtr window);
[DllImport("android")]
internal static extern int ANativeWindow_lock(IntPtr window, out ANativeWindow_Buffer outBuffer, ref ARect inOutDirtyBounds);
public enum AndroidPixelFormat
{
WINDOW_FORMAT_RGBA_8888 = 1,
WINDOW_FORMAT_RGBX_8888 = 2,
WINDOW_FORMAT_RGB_565 = 4,
}
internal struct ARect
{
public int left;
public int top;
public int right;
public int bottom;
}
internal struct ANativeWindow_Buffer
{
// The number of pixels that are show horizontally.
public int width;
// The number of pixels that are shown vertically.
public int height;
// The number of *pixels* that a line in the buffer takes in
// memory. This may be >= width.
public int stride;
// The format of the buffer. One of WINDOW_FORMAT_*
public AndroidPixelFormat format;
// The actual bits.
public IntPtr bits;
// Do not touch.
uint reserved1;
uint reserved2;
uint reserved3;
uint reserved4;
uint reserved5;
uint reserved6;
}
public Screen Screen
{
get
{
return generalView.Screen;
}
}
public float RenderScaling
{
get
{
//return global::Android.App.Application.Context.Resources.DisplayMetrics.ScaledDensity;
return generalView.RenderScaling;
}
}
public float LayoutScaling => RenderScaling;
public Action ScalingChanged { get => generalView.ScalingChanged; set => generalView.ScalingChanged = value; }
public Action<Size> Resized { get => generalView.Resized; set => generalView.Resized = value; }
Action<PixelPoint> IViewImpl.PositionChanged { get => generalView.PositionChanged; set => generalView.PositionChanged = value; }
Action IViewImpl.Activated { get => generalView.Activated; set => generalView.Activated = value; }
public Action Deactivated { get => generalView.Deactivated; set => generalView.Deactivated = value; }
bool IViewImpl.CanActivate { get => generalView.CanActivate; set => generalView.CanActivate = value; }
PixelPoint IViewImpl.Position
{
get
{
return generalView.Position;
}
set
{
generalView.Position = value;
}
}
void IViewImpl.Activate()
{
//throw new NotImplementedException();
//RequestFocus();
generalView.Activate();
}
void IViewImpl.Capture()
{
//RequestPointerCapture();
//throw new NotImplementedException();
generalView.Capture();
}
void IViewImpl.Invalidate(in Drawing.Rect rect)
{
generalView.Invalidate(in rect);
}
Drawing.Point IViewImpl.PointToClient(Drawing.Point point)
{
return generalView.PointToClient(point);
}
Drawing.Point IViewImpl.PointToScreen(Drawing.Point point)
{
return generalView.PointToScreen(point);
}
void IViewImpl.ReleaseCapture()
{
generalView.ReleaseCapture();
}
void IViewImpl.SetCursor(Cursor cursor)
{
//throw new NotImplementedException();
generalView.SetCursor(cursor);
}
//internal bool IMEEnable = true;
void IViewImpl.SetIMEEnable(bool enable)
{
//IMEEnable = enable;
generalView.SetIMEEnable(enable);
}
//bool showInput;
//internal static IEditor editor;
public void ShowKeyboard(bool show, IEditor editor)
{
generalView.ShowKeyboard(show, editor);
}
void IViewImpl.SetIMEPosition(Drawing.Point point)
{
}
void IViewImpl.SetRoot(Controls.View view)
{
generalView.SetRoot(view);
}
void IViewImpl.SetVisible(bool visible)
{
generalView.SetVisible(visible);
}
protected override void Dispose(bool disposing)
{
eglContext?.Dispose();
eglContext = null;
base.Dispose(disposing);
}
public void SurfaceChanged(ISurfaceHolder holder, [GeneratedEnum] Format format, int width, int height)
{
pixelSize = new PixelSize(width, height);
eglContext.OnDestroySurface();
eglContext.OnCreateSurface();
//GLES20.GlViewport(0, 0, width, height);
}
public void SurfaceCreated(ISurfaceHolder holder)
{
eglContext?.OnCreateSurface();
}
public void SurfaceDestroyed(ISurfaceHolder holder)
{
eglContext?.OnDestroySurface();
}
//public bool OnCapturedPointer(View view, MotionEvent e)
//{
// System.Diagnostics.Debug.WriteLine(view + "--" + e.Action);
// return false;
//}
}
class InnerView : CPF.Controls.View
{
ISurfaceView cpfView;
public InnerView(ISurfaceView cpfView) : base(cpfView)
{
this.cpfView = cpfView;
}
protected override IViewImpl CreateView()
{
return cpfView;
}
}
//class TestView : View
//{
// public TestView(global::Android.Content.Context context) : base(context)
// {
// Focusable = true;
// FocusableInTouchMode = true;
// }
//}
}

View File

@ -0,0 +1,163 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{6255E6D7-7EE3-4CB9-AF6C-F75B52341294}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{9ef11e43-1701-4396-8835-8392d57abb70}</TemplateGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CPF.Android</RootNamespace>
<AssemblyName>CPF.Android</AssemblyName>
<FileAlignment>512</FileAlignment>
<Deterministic>True</Deterministic>
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v9.0</TargetFrameworkVersion>
<AndroidUseAapt2>true</AndroidUseAapt2>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>bin\Debug\CPF.Android.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>portable</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Java.Interop" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="Mono.Android" />
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Xamarin.Essentials" Version="1.6.1" />
</ItemGroup>
<ItemGroup>
<Compile Include="ClipboardImpl.cs" />
<Compile Include="CpfAndroidApp.cs" />
<Compile Include="AndroidPlatform.cs" />
<Compile Include="AndroidSynchronizationContext.cs" />
<Compile Include="AndroidView.cs" />
<Compile Include="CpfActivity.cs" />
<Compile Include="CpfView.cs" />
<Compile Include="EglContext.cs" />
<Compile Include="FileSaveFragment.cs" />
<Compile Include="GeneralView.cs" />
<Compile Include="InputConnection.cs" />
<Compile Include="ISurfaceView.cs" />
<Compile Include="ListBoxTemplate.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="NativeImpl.cs" />
<Compile Include="OpenFileDialogView.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="PopupImpl.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Resources\Resource.designer.cs" />
<Compile Include="OpenGLView.cs" />
<Compile Include="SoftKeyboardListner.cs" />
<Compile Include="WindowImpl.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CPF\CPF.csproj">
<Project>{5c1e41fd-fd6b-4107-b429-1db7bd5c37d5}</Project>
<Name>CPF</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\filedialog_folder_l.png">
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\filedialog_folder_m.png">
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\filedialog_folder_s.png">
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\filedialog_folder_up_l.png">
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\filedialog_folder_up_m.png">
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\filedialog_folder_up_s.png">
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\filedialog_root_l.png">
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\filedialog_root_m.png">
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\filedialog_root_s.png">
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\ic_about.png">
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\ic_launcher.png">
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\values\strings.xml">
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<Content Include="license.txt" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<!--打包Nuget的时候会找不到只能用下面的绝对路径-->
<!--<Import Project="C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Xamarin\Android\Xamarin.Android.CSharp.targets" />-->
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<package >
<metadata>
<id>Xhm.CPF.Android</id>
<version>0.9.6</version>
<authors>QQ:761716178</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<projectUrl>http://cpf.cskin.net/</projectUrl>
<iconUrl>https://www.nuget.org/Content/gallery/img/default-package-icon.svg</iconUrl>
<description>CPF(Cross platform UI framework) QQ:761716178 跨平台UI框架 http://cpf.cskin.net/</description>
<releaseNotes>CPF安卓支持预览版</releaseNotes>
<copyright>Copyright (c) 2020 by http://cpf.cskin.net/</copyright>
<tags>CPF Android</tags>
<dependencies>
<dependency id="Xamarin.Essentials" version="1.6.1" />
<dependency id="Xhm.CPF" version="0.9.6" />
</dependencies>
</metadata>
</package>

View File

@ -0,0 +1,182 @@
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using CPF.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
namespace CPF.Android
{
class ClipboardImpl : CPF.Input.IClipboard
{
private Context context = CpfActivity.CurrentActivity;
private ClipboardManager ClipboardManager
{
get
{
return this.context.GetSystemService(Context.ClipboardService).JavaCast<ClipboardManager>();
}
}
public void Clear()
{
ClipboardManager.PrimaryClip = null;
}
public bool Contains(DataFormat dataFormat)
{
var pc = ClipboardManager.PrimaryClip;
if (pc != null && ClipboardManager.HasPrimaryClip)
{
for (int i = 0; i < pc.ItemCount; i++)
{
var data = pc.GetItemAt(i);
if (dataFormat == DataFormat.Text)
{
if (data.Text != null)
{
return true;
}
}
else if (dataFormat == DataFormat.Html)
{
if (data.HtmlText != null)
{
return true;
}
}
else if (dataFormat == DataFormat.FileNames)
{
if (data.Uri != null)
{
return true;
}
}
}
}
return false;
}
public object GetData(DataFormat dataFormat)
{
var pc = ClipboardManager.PrimaryClip;
if (pc != null && ClipboardManager.HasPrimaryClip)
{
for (int i = 0; i < pc.ItemCount; i++)
{
var data = pc.GetItemAt(i);
switch (dataFormat)
{
case DataFormat.Text:
return data.Text;
case DataFormat.Html:
return data.HtmlText;
case DataFormat.FileNames:
var file = data.Uri?.ToString();
if (file != null)
{
return new string[] { file };
}
break;
}
}
}
return null;
}
public void SetData(params (DataFormat, object)[] data)
{
ClipData clipData = null;
foreach (var item in data)
{
switch (item.Item1)
{
case DataFormat.Text:
if (clipData == null)
{
clipData = ClipData.NewPlainText("text", item.Item2 as string);
}
else
{
clipData.AddItem(new ClipData.Item(item.Item2 as string));
}
break;
case DataFormat.Html:
var text = data.FirstOrDefault(a => a.Item1 == DataFormat.Text);
var str = text.Item1 != DataFormat.Unknown && text.Item2 != null ? text.Item2 as string : NoHTML(item.Item2 as string);
if (clipData == null)
{
clipData = ClipData.NewHtmlText("html", str, item.Item2 as string);
}
else
{
clipData.AddItem(new ClipData.Item(str, item.Item2 as string));
}
break;
case DataFormat.FileNames:
foreach (var file in item.Item2 as IEnumerable<string>)
{
if (clipData == null)
{
clipData = ClipData.NewRawUri("URI", global::Android.Net.Uri.Parse(file));
}
else
{
clipData.AddItem(new ClipData.Item(global::Android.Net.Uri.Parse(file)));
}
}
break;
}
}
}
/// <summary>
/// 去除HTML标记
/// </summary>
/// <param name=”NoHTML”>包括HTML的源码 </param>
/// <returns>已经去除后的文字</returns>
public static string NoHTML(string Htmlstring)
{
//删除脚本
Htmlstring = Regex.Replace(Htmlstring, @"<script[^>]*?>.*?</script>", "",
RegexOptions.IgnoreCase);
//删除HTML
Htmlstring = Regex.Replace(Htmlstring, @"<(.[^>]*)>", "",
RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"([\r\n])[\s]+", "",
RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @">", "", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"<!.*", "", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(quot|#34);", "\"",
RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(amp|#38);", "&",
RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(lt|#60);", "<",
RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(gt|#62);", ">",
RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(nbsp|#160);", " ",
RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(iexcl|#161);", "\xa1", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(cent|#162);", "\xa2", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(pound|#163);", "\xa3", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(copy|#169);", "\xa9", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&#(\d+);", "", RegexOptions.IgnoreCase);
Htmlstring.Replace("<", "");
Htmlstring.Replace(">", "");
Htmlstring.Replace("\r\n", "");
Htmlstring = WebUtility.HtmlEncode(Htmlstring).Trim();
return Htmlstring;
}
}
}

113
CPF.Android/CpfActivity.cs Normal file
View File

@ -0,0 +1,113 @@
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using static Java.Lang.Thread;
using Java.Lang;
using System.Threading.Tasks;
using static CPF.Android.FileSaveFragment;
using Android.Content.PM;
namespace CPF.Android
{
public class CpfActivity : Activity, IUncaughtExceptionHandler//, FileSaveCallbacks
{
public CpfActivity()
{
Java.Lang.Thread.DefaultUncaughtExceptionHandler = this;
if (CurrentActivity == null)
{
CurrentActivity = this;
CPF.Platform.Application.Run(new CpfAndroidApp());
}
}
/// <summary>
/// 当前活动的Activity
/// </summary>
public static Activity CurrentActivity
{
get;
private set;
}
protected override void OnCreate(Bundle savedInstanceState)
{
RequestWindowFeature(WindowFeatures.NoTitle);
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
}
protected override void OnResume()
{
CurrentActivity = this;
base.OnResume();
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
protected override void OnDestroy()
{
base.OnDestroy();
if (CurrentActivity == this)
{
CurrentActivity = null;
}
}
/// <summary>
/// 申请弹窗权限可以弹到桌面需要添加SYSTEM_ALERT_WINDOW 权限
/// </summary>
public void ApplyDrawOverLays()
{
Intent intent = new Intent();
intent.SetAction(global::Android.Provider.Settings.ActionManageOverlayPermission);
intent.SetData(global::Android.Net.Uri.Parse("package:" + PackageName));
StartActivity(intent);
}
public virtual void UncaughtException(Thread t, Throwable e)
{
Console.WriteLine("java异常线程" + t.Name + " 堆栈:" + e.StackTrace);
}
//internal TaskCompletionSource<string[]> fileName;
//protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
//{
// if (requestCode == 20)
// {
// var uri = data?.Data as global::Android.Net.Uri;
// if (fileName != null)
// {
// if (uri == null)
// {
// fileName.SetResult(new string[] {});
// }
// else
// {
// fileName.SetResult(new string[] { uri.Path });
// }
// }
// }
// base.OnActivityResult(requestCode, resultCode, data);
//}
//public bool onCanSave(string absolutePath, string fileName)
//{
// return true;
//}
//public void onConfirmSave(string absolutePath, string fileName)
//{
//}
public CpfView Main { get; set; }
}
}

View File

@ -0,0 +1,31 @@
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CPF.Android
{
public class CpfAndroidApp : CPF.Platform.IApp
{
public bool IsMain { get; set; }
public event EventHandler Closed;
public void Close()
{
Closed(this, EventArgs.Empty);
Java.Lang.JavaSystem.Exit(0);
}
public void Show()
{
}
}
}

124
CPF.Android/CpfView.cs Normal file
View File

@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
namespace CPF.Android
{
/// <summary>
/// 用于承载CPF内容的容器
/// </summary>
public class CpfView : AbsoluteLayout, View.IOnLayoutChangeListener
{
internal ISurfaceView androidView;
/// <summary>
/// 用于承载CPF内容的容器
/// </summary>
/// <param name="activity"></param>
/// <param name="content"></param>
public CpfView(Context activity, UIElement content) : base(activity)
{
AddOnLayoutChangeListener(this);
//if (CPF.Platform.Application.GetDrawingFactory().UseGPU)
//{
// androidView = new OpenGLView(activity, content, this);
//}
//else
//{
androidView = new AndroidView(activity, content, this);
//}
////AddView(androidView);
if (activity is CpfActivity cpf && cpf.Main == null)
{
cpf.Main = this;
}
}
/// <summary>
/// 用于承载CPF内容的容器
/// </summary>
/// <param name="content"></param>
public CpfView(UIElement content) : base(CpfActivity.CurrentActivity)
{
//if (CPF.Platform.Application.GetDrawingFactory().UseGPU)
//{
// androidView = new OpenGLView(CpfActivity.CurrentActivity, content, this);
//}
//else
//{
androidView = new AndroidView(CpfActivity.CurrentActivity, content, this);
//}
////AddView(androidView);
if (CpfActivity.CurrentActivity is CpfActivity cpf && cpf.Main == null)
{
cpf.Main = this;
}
}
public CPF.Controls.View Root
{
get { return androidView.Root; }
}
public WindowManagerTypes WindowType { get; set; } = WindowManagerTypes.DrawnApplication;
public WindowManagerFlags WindowFlags { get; set; } = WindowManagerFlags.LayoutNoLimits;
PixelPoint position;
public PixelPoint Location
{
get { return position; }
protected set
{
position = value;
(androidView as CPF.Platform.IViewImpl).PositionChanged(position);
OnMove();
}
}
protected virtual void OnMove()
{
}
public void OnLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)
{
if (!(this is WindowImpl) && !(this is PopupImpl))
{
Location = new PixelPoint(left, top);
}
}
public void UpdateLayout(int x, int y, int w, int h)
{
var margin = new WindowManagerLayoutParams(WindowType, WindowFlags, global::Android.Graphics.Format.Rgbx8888);
margin.Gravity = GravityFlags.Left | GravityFlags.Top;
margin.X = x;
margin.Y = y;
margin.Width = w;
margin.Height = h;
margin.WindowAnimations = -1;
//var layoutParamsClass = Java.Lang.Class.ForName("android.view.WindowManager$LayoutParams");
var layoutParamsClass = margin.Class;
var privateFlags = layoutParamsClass.GetField("privateFlags");
var noAnim = layoutParamsClass.GetField("PRIVATE_FLAG_NO_MOVE_ANIMATION");
int privateFlagsValue = privateFlags.GetInt(margin);
int noAnimFlag = noAnim.GetInt(margin);
privateFlagsValue |= noAnimFlag;
privateFlags.SetInt(margin, privateFlagsValue);
(Context as Activity).WindowManager.UpdateViewLayout(this, margin);
//LayoutParameters = margin;
Location = new PixelPoint(x, y);
//System.Diagnostics.Debug.WriteLine($"{x},{y}");
}
}
}

146
CPF.Android/EglContext.cs Normal file
View File

@ -0,0 +1,146 @@
using Android.App;
using Android.Content;
using Android.Graphics;
//using Android.Opengl;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Views.InputMethods;
using Android.Widget;
using CPF.Drawing;
using CPF.Input;
using CPF.OpenGL;
using CPF.Platform;
using Javax.Microedition.Khronos.Egl;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GLES20 = global::Android.Opengl.GLES20;
namespace CPF.Android
{
class EglContext : IGlContext
{
EGLDisplay display;
IEGL10 egl;
EGLSurface eglSurface;
EGLContext context;
EGLConfig config;
AndroidView androidView;
public EglContext(AndroidView androidView)
{
this.androidView = androidView;
//1. 取得EGL实例
egl = EGLContext.EGL.JavaCast<IEGL10>();
//2. 选择Display
display = egl.EglGetDisplay(EGL10.EglDefaultDisplay);
egl.EglInitialize(display, null);
int[] attribList = {
EGL10.EglRedSize, 8,
EGL10.EglGreenSize, 8,
EGL10.EglBlueSize, 8,
EGL10.EglAlphaSize, 8,
EGL10.EglRenderableType,global:: Android.Opengl.EGL14.EglOpenglEs2Bit,
EGL10.EglStencilSize, 8, // placeholder for recordable [@-3]
EGL10.EglNone
};
//3. 选择Config
EGLConfig[] configs = new EGLConfig[1];
int[] numConfigs = new int[1];
egl.EglChooseConfig(display, attribList, configs, configs.Length, numConfigs);
config = configs[0];
// 创建Context
context = egl.EglCreateContext(display, config, EGL10.EglNoContext, new int[]{
global:: Android.Opengl.EGL14.EglContextClientVersion, 2,
EGL10.EglNone
});
}
public EGLSurface EglSurface
{
get { return eglSurface; }
}
//IntPtr window;
public void OnCreateSurface()
{
//window = AndroidView.ANativeWindow_fromSurface(JNIEnv.Handle, androidView.Holder.Surface.Handle);
// 创建Surface
eglSurface = egl.EglCreateWindowSurface(display, config, androidView, null);
}
public void OnDestroySurface()
{
//AndroidView.ANativeWindow_release(window);
if (eglSurface != null)
{
egl.EglDestroySurface(display, eglSurface);
}
eglSurface = null;
}
EGLContext oldContext;
EGLDisplay oldDisplay;
EGLSurface oldReadSurface;
EGLSurface oldDrawSurface;
public void MakeCurrent()
{
if (eglSurface != null)
{
oldContext = egl.EglGetCurrentContext();
oldDisplay = egl.EglGetCurrentDisplay();
oldReadSurface = egl.EglGetCurrentSurface(global::Android.Opengl.EGL14.EglRead);
oldDrawSurface = egl.EglGetCurrentSurface(global::Android.Opengl.EGL14.EglDraw);
egl.EglMakeCurrent(display, eglSurface, eglSurface, context);
}
}
public void SwapBuffers()
{
if (eglSurface != null)
{
egl.EglSwapBuffers(display, eglSurface);
//egl.EglMakeCurrent(display, EGL10.EglNoSurface, EGL10.EglNoSurface, EGL10.EglNoContext);
egl.EglMakeCurrent(oldDisplay, oldDrawSurface, oldReadSurface, oldContext);
}
}
public IDisposable GRContext { get; set; }
public void Dispose()
{
OnDestroySurface();
if (display != null && context != null)
{
if (display.Handle != IntPtr.Zero && context.Handle != IntPtr.Zero)
{
egl.EglDestroyContext(display, context);
}
egl.EglTerminate(display);
egl.Dispose();
context = null;
display = null;
}
GRContext?.Dispose();
GRContext = null;
}
public void GetFramebufferInfo(out int framebuffer, out int samples, out int stencil)
{
var buffer = new int[3];
GLES20.GlGetIntegerv(GLES20.GlFramebufferBinding, buffer, 0);
GLES20.GlGetIntegerv(GLES20.GlStencilBits, buffer, 1);
GLES20.GlGetIntegerv(GLES20.GlSamples, buffer, 2);
samples = buffer[2];
stencil = buffer[1];
framebuffer = buffer[0];
}
public IntPtr GetProcAddress(string name)
{
return OpenGLView.eglGetProcAddress(name);
}
}
}

View File

@ -0,0 +1,512 @@
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Java.IO;
using Android.Graphics;
using Android.Graphics.Drawables;
using static Android.Widget.AdapterView;
namespace CPF.Android
{
public class FileSaveFragment : DialogFragment, IOnItemClickListener, IDialogInterfaceOnClickListener, View.IOnClickListener
{
private static string TAG = "FileSaveFragment";
/*
* Use the unicode "back" triangle to indicate there is a parent directory
* rather than an icon to minimise file dependencies.
*
* You may have to find an alternative symbol if the font in use doesn't
* support this character.
*/
static string PARENT = "\u25C0";
private FileSaveCallbacks mCallbacks;
private List<File> directoryList;
private string defaultExtension;
// The widgets required to provide the UI.
private TextView currentPath;
private EditText fileName;
private LinearLayout root;
private ListView directoryView;
// The directory the user has selected.
private File currentDirectory;
// Resource IDs
private int resourceID_OK;
private int resourceID_Cancel;
private int resourceID_Title;
private int resourceID_EditHint;
private int resourceID_Icon;
private int dialog_Height;
private int resourceID_Dir;
private int resourceID_UpDir;
private int resourceID_File;
/**
* Does file already exist?
* */
public static bool FileExists(string absolutePath, string fileName)
{
File checkFile = new File(absolutePath, fileName);
return checkFile.Exists();
}
/**
* Restrict valid filenames to alpha-numeric (word chars) only. Simplifies
* reserved path character validation at cost of forbidding spaces, hyphens
* and underscores.
*
* @param fileName
* - filename without extension or path information.
*
* */
public static bool IsAlphaNumeric(string fileName)
{
fileName = NameNoExtension(fileName);
return (!Regex.IsMatch(fileName, ".*\\W{1,}.*"));
}
/**
* Return the characters following the final full stop in the filename.
* */
public static string Extension(string fileName)
{
string extension = "";
if (fileName.Contains("."))
{
string[] tokens = Regex.Split(fileName, "\\.(?=[^\\.]+$)");
extension = tokens[1];
}
return extension;
}
/**
* Return the filename without any extension. Extension is taken to be the
* characters following the final full stop (if any) in the filename.
*
* @param fileName
* - File name with or without extension.
* */
public static string NameNoExtension(string fileName)
{
if (fileName.Contains("."))
{
String[] tokens = Regex.Split(fileName, "\\.(?=[^\\.]+$)");
fileName = tokens[0];
}
return fileName;
}
/**
* Signal to / request action of host activity.
*
* */
public interface FileSaveCallbacks
{
/**
* Hand potential file details to context for validation.
*
* @param absolutePath
* - Absolute path to target directory.
* @param fileName
* - Filename. Not guaranteed to have a type extension.
*
* */
public bool onCanSave(string absolutePath, string fileName);
/**
* Hand validated path and name to context for use. If user cancels
* absolutePath and filename are handed out as null.
*
* @param absolutePath
* - Absolute path to target directory.
* @param fileName
* - Filename. Not guaranteed to have a type extension.
* */
public void onConfirmSave(string absolutePath, string fileName);
}
/**
* Create new instance of a file save popup.
*
* @param defaultExtension
* - Display a default extension for file to be created. Can be
* null.
* @param resourceID_OK
* - string resource ID for the positive (OK) button.
* @param resourceID_Cancel
* - string resource ID for the negative (Cancel) button.
* @param resourceID_Title
* - string resource ID for the dialogue's title.
* @param resourceID_EditHint
* - string resource ID for the filename edit widget.
* @param resourceID_Icon
* - Drawable resource ID for the dialogue's title bar icon.
* */
public static FileSaveFragment newInstance(string defaultExtension,
int resource_DialogHeight, int resourceID_OK,
int resourceID_Cancel, int resourceID_Title,
int resourceID_EditHint, int resourceID_Icon,
int resourceID_Directory, int resourceID_UpDirectory,
int resourceID_File)
{
FileSaveFragment frag = new FileSaveFragment();
Bundle args = new Bundle();
args.PutString("extensionList", defaultExtension);
args.PutInt("captionOK", resourceID_OK);
args.PutInt("captionCancel", resourceID_Cancel);
args.PutInt("popupTitle", resourceID_Title);
args.PutInt("editHint", resourceID_EditHint);
args.PutInt("popupIcon", resourceID_Icon);
args.PutInt("dialogHeight", resource_DialogHeight);
args.PutInt("iconDirectory", resourceID_Directory);
args.PutInt("iconUpDirectory", resourceID_UpDirectory);
args.PutInt("iconFile", resourceID_File);
frag.Arguments = args;
return frag;
}
/**
* Note the parent activity for callback purposes.
*
* @param activity
* - parent activity
* */
public override void OnAttach(Activity activity)
{
base.OnAttach(activity);
// The containing activity is expected to implement the fragment's
// callbacks otherwise it can't react to item changes.
if (!(activity is FileSaveCallbacks))
{
throw new Exception(
"Activity must implement fragment's callbacks.");
}
mCallbacks = (FileSaveCallbacks)activity;
directoryList = new List<File>();
defaultExtension = Arguments.GetString("extensionList");
resourceID_OK = Arguments.GetInt("captionOK");
resourceID_Cancel = Arguments.GetInt("captionCancel");
resourceID_Title = Arguments.GetInt("popupTitle");
resourceID_EditHint = Arguments.GetInt("editHint");
resourceID_Icon = Arguments.GetInt("popupIcon");
dialog_Height = Arguments.GetInt("dialogHeight");
resourceID_File = Arguments.GetInt("iconFile");
resourceID_Dir = Arguments.GetInt("iconDirectory");
resourceID_UpDir = Arguments.GetInt("iconUpDirectory");
}
/**
* Build the popup.
* */
public override Dialog OnCreateDialog(Bundle savedInstanceState)
{
/*
* Use the same callback for [OK] & [Cancel]. Hand out nulls to indicate
* abandonment.
*/
/*
* We want to make this a transportable piece of code so don't want an
* XML layout dependency so layout is set up in code.
*
* [ListView of directory names ] [ ] [ ] [ ]
* ------------------------------------------------------ {current
* path}/ [ Enter Filename ] {default extension}
*/
// Set up the container view.
LinearLayout.LayoutParams rootLayout = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MatchParent,
ViewGroup.LayoutParams.WrapContent, 0.0F);
root = new LinearLayout(Activity);
root.Orientation = Orientation.Vertical;
root.LayoutParameters = rootLayout;
/*
* Set up initial sub-directory list.
*/
currentDirectory = global::Android.OS.Environment.ExternalStorageDirectory;
directoryList = getSubDirectories(currentDirectory);
DirectoryDisplay displayFormat = new DirectoryDisplay(Activity,
directoryList, this);
/*
* Fix the height of the listview at 150px, enough to show 3 or 4
* entries at a time. Don't want the popup shrinking and growing all the
* time. Tried it. Most disconcerting.
*/
LinearLayout.LayoutParams listViewLayout = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MatchParent, dialog_Height, 0.0F);
directoryView = new ListView(Activity);
directoryView.LayoutParameters = listViewLayout;
directoryView.Adapter = displayFormat;
directoryView.OnItemClickListener = this;
root.AddView(directoryView);
View horizDivider = new View(Activity);
horizDivider.SetBackgroundColor(Color.Cyan);
root.AddView(horizDivider, new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MatchParent, 2));
/*
* Now set up the filename entry area.
*
* {current path}/ [Enter Filename ] {default extension}
*/
LinearLayout nameArea = new LinearLayout(Activity);
nameArea.Orientation = Orientation.Horizontal;
nameArea.LayoutParameters = rootLayout;
root.AddView(nameArea);
currentPath = new TextView(Activity);
currentPath.SetText(currentDirectory.AbsolutePath + "/", TextView.BufferType.Normal);
nameArea.AddView(currentPath);
/*
* We want the filename input area to be as large as possible, but still
* leave enough room to show the path and any default extension that may
* be supplied so we give it a weight of 1.
*/
LinearLayout.LayoutParams fileNameLayout = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WrapContent,
ViewGroup.LayoutParams.WrapContent, 1.0F);
fileName = new EditText(Activity);
fileName.Hint = resourceID_EditHint.ToString();
fileName.Gravity = GravityFlags.Left;
fileName.LayoutParameters = fileNameLayout;
fileName.InputType = global::Android.Text.InputTypes.TextFlagNoSuggestions;
nameArea.AddView(fileName);
/*
* We only display the default extension if one has been supplied.
*/
if (defaultExtension != null)
{
TextView defaultExt = new TextView(Activity);
defaultExt.Text = defaultExtension;
defaultExt.Gravity = GravityFlags.Left;
defaultExt.SetPadding(2, 0, 6, 0);
nameArea.AddView(defaultExt);
}
// Use the standard AlertDialog builder to create the popup.
// Android custom and practice is normally to chain calls from the
// builder, but
// it can become an unreadable and unmaintainable mess very quickly so I
// don't.
var popupBuilder = new AlertDialog.Builder(Activity);
popupBuilder.SetView(root);
popupBuilder.SetIcon(resourceID_Icon);
popupBuilder.SetTitle(resourceID_Title);
// // Set up anonymous methods to handle [OK] & [Cancel] click.
// popupBuilder.SetPositiveButton(resourceID_OK,
// new DialogInterface.OnClickListener()
// {
// public void onClick(DialogInterface dialog, int whichButton)
// {
// // Empty method. Method defined in onStart();
// }
//});
popupBuilder.SetPositiveButton(resourceID_OK, this);
popupBuilder.SetNegativeButton(resourceID_Cancel, this);
return popupBuilder.Create();
}
public void OnClick(IDialogInterface dialog, int which)
{
mCallbacks.onConfirmSave(null, null);
}
/**
* Provide the [PositiveButton] with a click listener that doesn't dismiss
* the popup if the user has entered an invalid filename.
*
* */
public override void OnStart()
{
base.OnStart();
AlertDialog d = (AlertDialog)Dialog;
if (d != null)
{
Button positiveButton = d
.GetButton((int)DialogButtonType.Positive);
positiveButton.SetOnClickListener(this);
}
}
public void OnClick(View v)
{
string absolutePath = currentDirectory.AbsolutePath;
string filename = fileName.Text + defaultExtension;
if (mCallbacks.onCanSave(absolutePath, filename))
{
Dismiss();
mCallbacks.onConfirmSave(absolutePath, filename);
}
}
/**
* Identify all sub-directories within a directory.
*
* @param directory
* The directory to walk.
* */
private List<File> getSubDirectories(File directory)
{
List<File> directories = new List<File>();
File[] files = directory.ListFiles();
// Allow navigation back up the tree when the directory is a
// sub-directory.
if (directory.Parent != null)
{
directories.Add(new File(PARENT));
}
// Enumerate any sub-directories in this directory.
if (files != null)
{
foreach (File f in files)
{
if (f.IsDirectory && !f.IsHidden)
{
directories.Add(f);
}
}
}
return directories;
}
/**
* Refresh the listview's display adapter using the content of the
* identified directory.
*
* */
public void OnItemClick(AdapterView parent, View view, int pos, long id)
{
File selected = null;
if (pos >= 0 || pos < directoryList.Count)
{
selected = directoryList[pos];
string name = selected.Name;
// Are we going up or down?
if (name.Equals(PARENT))
{
currentDirectory = currentDirectory.ParentFile;
}
else
{
currentDirectory = selected;
}
// Refresh the listview display for the newly selected directory.
directoryList = getSubDirectories(currentDirectory);
DirectoryDisplay displayFormatter = new DirectoryDisplay(
Activity, directoryList, this);
directoryView.Adapter = displayFormatter;
// Update the path TextView widget. Tell the user where he or she
// is.
string path = currentDirectory.AbsolutePath;
if (currentDirectory.Parent != null)
{
path += "/";
}
currentPath.Text = path;
}
}
/**
* Display the sub-directories in a selected directory.
*
* */
private class DirectoryDisplay : ArrayAdapter<File>
{
FileSaveFragment fragment;
public DirectoryDisplay(Context context, List<File> displayContent, FileSaveFragment fragment) : base(context, global::Android.Resource.Layout.SimpleListItem1, displayContent)
{
this.fragment = fragment;
}
/**
* Display the name of each sub-directory.
* */
public override View GetView(int position, View convertView, ViewGroup parent)
{
int iconID = fragment.resourceID_File;
// We assume that we've got a parent directory...
TextView textview = (TextView)base.GetView(position, convertView,
parent);
// If we've got a directory then get its name.
if (fragment.directoryList[position] != null)
{
textview.Text = fragment.directoryList[position].Name;
if (fragment.directoryList[position].IsDirectory)
{
iconID = fragment.resourceID_Dir;
}
string name = fragment.directoryList[position].Name;
if (name.Equals(PARENT))
{
// iconID = -1;
iconID = fragment.resourceID_UpDir;
}
// Icon to the left of the text.
if (iconID > 0)
{
Drawable icon = fragment.Activity.Resources.GetDrawable(
iconID);
textview.SetCompoundDrawablesWithIntrinsicBounds(icon,
null, null, null);
}
}
return textview;
}
}
}
}

771
CPF.Android/GeneralView.cs Normal file
View File

@ -0,0 +1,771 @@
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Views.InputMethods;
using Android.Widget;
using CPF.Drawing;
using CPF.Input;
using CPF.Platform;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CPF.Android
{
class GeneralView : IViewImpl
{
public GeneralView(ISurfaceView view, UIElement content)
{
owner = view as View;
CpfView = view.CpfView;
this.activity = owner.Context as Activity;
//owner.CpfView.AddView(owner);
owner.SetOnTouchListener(view);
owner.SetOnKeyListener(view);
owner.FocusableInTouchMode = true;
softKeyboardListner = new SoftKeyboardListner(owner);
owner.ViewTreeObserver.AddOnGlobalLayoutListener(softKeyboardListner);
this.content = content;
}
UIElement content;
public void Create()
{
if (content != null)
{
root = new InnerView(owner as ISurfaceView);
root.Background = CPF.Drawing.Color.White;
root.Children.Add(content);
root.Visibility = CPF.Visibility.Visible;
root.CanActivate = true;
root.LayoutUpdated += Root_LayoutUpdated;
scale = root.LayoutScaling;
}
}
View owner;
public CpfView CpfView { get; }
internal SoftKeyboardListner softKeyboardListner;
CPF.Drawing.Rect? layoutRect;
bool isLayout;
private void Root_LayoutUpdated(object sender, RoutedEventArgs e)
{
var b = new CPF.Drawing.Rect(owner.Left, owner.Top, owner.Width, owner.Height);
if (CpfView.Handle != default)
{
b.X = CpfView.Left;
b.Y = CpfView.Top;
var s = root.ActualSize;
var l = root.ActualOffset;
var scaling = LayoutScaling;
if ((int)b.Width != (int)(s.Width * scaling) || (int)b.Height != (int)(s.Height * scaling) || (int)b.X != (int)(l.X * scaling) || (int)b.Y != (int)(l.Y * scaling))
{
var r = new CPF.Drawing.Rect((l.X * scaling), (l.Y * scaling), (s.Width * scaling), (s.Height * scaling));
if (layoutRect.HasValue)
{
layoutRect = r;
if (isLayout)
{
return;
}
isLayout = true;
//由于安卓自带动画影响布局,只能延迟刷新布局
this.Delay(TimeSpan.FromMilliseconds(50), () =>
{
if ((CpfView is PopupImpl) && CpfView.WindowVisibility == ViewStates.Visible)
{
CpfView.UpdateLayout((int)layoutRect.Value.Left, (int)layoutRect.Value.Top, (int)layoutRect.Value.Width, (int)layoutRect.Value.Height);
}
isLayout = false;
});
}
else
{
layoutRect = r;
if ((CpfView is PopupImpl) && CpfView.WindowVisibility == ViewStates.Visible)
{
CpfView.UpdateLayout((int)r.Left, (int)r.Top, (int)r.Width, (int)r.Height);
}
}
//Bounds = new Rect(l.X * scaling, l.Y * scaling, s.Width * scaling, s.Height * scaling);
}
}
}
internal Activity activity;
CPF.Controls.View root;
PixelSize oldSize;
public CPF.Controls.View Root
{
get { return root; }
}
public void OnCreateDrawableState(int extraSpace)
{
Invalidate();
}
public Rect invalidateRect;
bool _invalidateQueued;
public void Invalidate()
{
Invalidate(new Rect(new Point(), root.ActualSize));
}
public void Invalidate(in Rect rect)
{
Rect all = new Rect(new Point(), root.ActualSize);//控件区域
if ((all.Contains(rect) || rect.IntersectsWith(all) || all == rect || rect.Contains(all)) && !rect.IsEmpty && !all.IsEmpty)
{
//Debug.WriteLine(invalidateRect);
if (invalidateRect.IsEmpty || rect.Contains(invalidateRect) || rect == invalidateRect)//如果更新区域大于原有失效区域,则当前失效区域设为更新区域
{
invalidateRect = rect;
}
else if (invalidateRect.Contains(rect))//如果更新区域小于原来的失效区域,则失效区域不变
{
}
else if (invalidateRect.IsEmpty)//如果原来的失效区域为空
{
invalidateRect = rect;
}
else
{//如果两个区域没有关联或者相交
var minX = invalidateRect.X < rect.X ? invalidateRect.X : rect.X;//确定包含这两个矩形的最小矩形
var minY = invalidateRect.Y < rect.Y ? invalidateRect.Y : rect.Y;
var maxW = (invalidateRect.Width + invalidateRect.X - minX) > (rect.Width + rect.X - minX) ? (invalidateRect.Width + invalidateRect.X - minX) : (rect.Width + rect.X - minX);
var maxH = (invalidateRect.Height + invalidateRect.Y - minY) > (rect.Height + rect.Y - minY) ? (invalidateRect.Height + invalidateRect.Y - minY) : (rect.Height + rect.Y - minY);
Rect min = new Rect(minX, minY, maxW, maxH);
invalidateRect = min;
}
invalidateRect.Intersect(all);//最后失效区域为在控件区域里面的相交区域
}
if (_invalidateQueued)
{
return;
}
_invalidateQueued = true;
Threading.Dispatcher.MainThread.BeginInvoke(() =>
{
_invalidateQueued = false;
if (CpfView.Handle != default)
{
OnPaint();
invalidateRect = new Rect();
}
});
}
float scale;
public void OnLayout(bool changed, int left, int top, int right, int bottom)
{
//base.OnLayout(changed, left, top, right, bottom);
var size = new PixelSize(right, bottom);
var sc = LayoutScaling;
if (LayoutScaling != scale)
{
scale = sc;
ScalingChanged();
}
if (size != oldSize)
{
oldSize = size;
Resized(new Size(size.Width / LayoutScaling, size.Height / LayoutScaling));
}
}
void OnPaint()
{
if (owner is AndroidView)
{
(owner as ISurfaceView).OnPaint();
}
else if (owner is OpenGLView)
{
//owner.Invalidate();
}
}
DateTime _lastTouchMoveEventTime;
Drawing.Point? _lastTouchMovePoint;
InputConnection inputConnection;
public IInputConnection OnCreateInputConnection(EditorInfo outAttrs)
{
outAttrs.InputType = (editor == null || editor.IsInputMethodEnabled) ? global::Android.Text.InputTypes.ClassText : global::Android.Text.InputTypes.TextVariationPassword;
outAttrs.ImeOptions = ImeFlags.NoFullscreen;
if (inputConnection == null)
{
inputConnection = new InputConnection(owner as ISurfaceView);
}
return inputConnection;
}
public bool OnKey(View v, [GeneratedEnum] Keycode keyCode, KeyEvent e)
{
if (e.Action != KeyEventActions.Multiple)
{
var routEvent = new CPF.Input.KeyEventArgs(root, ConvertKey(e.KeyCode), (int)e.KeyCode, root.InputManager.KeyboardDevice.Modifiers, root.InputManager.KeyboardDevice);
root.InputManager.KeyboardDevice.Modifiers = GetModifierKeys(e);
root.InputManager.KeyboardDevice.ProcessEvent(routEvent, e.Action == KeyEventActions.Down ? KeyEventType.KeyDown : KeyEventType.KeyUp);
if (e.Action == KeyEventActions.Down && e.UnicodeChar >= 32)
{
root.InputManager.KeyboardDevice.ProcessEvent(new TextInputEventArgs(root, root.InputManager.KeyboardDevice, Convert.ToChar(e.UnicodeChar).ToString()), KeyEventType.TextInput);
}
}
return false;
}
public bool OnTouch(View v, MotionEvent e)
{
CPF.Drawing.Point _point = new Drawing.Point(e.GetX(), e.GetY());
var position = new int[] { 0, 0 };
owner.GetLocationOnScreen(position);
double x = owner.GetX();
double y = owner.GetY();
(CPF.Platform.Application.GetRuntimePlatform() as AndroidPlatform).mousePosition = new PixelPoint((int)(e.RawX), (int)(e.RawY));
//System.Diagnostics.Debug.WriteLine($":视图在屏幕位置{position[0]},{position[1]} " +
// $" 鼠标位置{_point}、{e.RawX},{e.RawY} 鼠标在屏幕位置:{(CPF.Platform.Application.GetRuntimePlatform() as AndroidPlatform).mousePosition} 视图位置:{root.Position} {Parent.IsLayoutRequested}");
EventType? mouseEventType = null;
EventType touchEvent = EventType.TouchDown;
var eventTime = DateTime.Now;
//Basic touch support
switch (e.Action)
{
case MotionEventActions.Move:
//may be bot flood the evnt system with too many event especially on not so powerfull mobile devices
if ((eventTime - _lastTouchMoveEventTime).TotalMilliseconds > 10)
{
mouseEventType = EventType.MouseMove;
touchEvent = EventType.TouchMove;
}
break;
case MotionEventActions.Down:
case MotionEventActions.PointerDown:
mouseEventType = EventType.MouseDown;
touchEvent = EventType.TouchDown;
_lastTouchMoveEventTime = eventTime;
break;
case MotionEventActions.Up:
case MotionEventActions.PointerUp:
mouseEventType = EventType.MouseUp;
touchEvent = EventType.TouchUp;
break;
}
InputEventArgs routedEvent = null;
if (mouseEventType != null)
{
//double r = x + owner.Width;
//double b = y + owner.Height;
//if (x <= _point.X && r >= _point.X && y <= _point.Y && b >= _point.Y)
{
routedEvent = new MouseButtonEventArgs(root, mouseEventType != EventType.MouseUp, false, false, _point / root.LayoutScaling, root.InputManager.MouseDevice, MouseButton.Left, true);
//System.Diagnostics.Debug.WriteLine(_point / root.LayoutScaling + " " + mouseEventType.Value);
root.InputManager.MouseDevice.ProcessEvent(routedEvent, root.LayoutManager.VisibleUIElements, mouseEventType.Value);
root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint { Id = e.ActionIndex, Position = _point / root.LayoutScaling }, root.InputManager.TouchDevice, root), root.LayoutManager.VisibleUIElements, touchEvent);
if (e.Action == MotionEventActions.Move && root.InputManager.MouseDevice.Captured == null)
{
if (_lastTouchMovePoint != null)
{
////raise mouse scroll event so the scrollers
////are moving with the cursor
//double vectorX = _point.X - _lastTouchMovePoint.Value.X;
//double vectorY = _point.Y - _lastTouchMovePoint.Value.Y;
////based on test correction of 0.02 is working perfect
//double correction = 1;
//var ps = root.LayoutScaling;
//root.InputManager.MouseDevice.ProcessEvent(new MouseWheelEventArgs(root, false, false, false, _point / root.LayoutScaling, root.InputManager.MouseDevice, new Vector((float)(vectorX * correction / ps), (float)(vectorY * correction / ps)), true), root.LayoutManager.VisibleUIElements, EventType.MouseWheel);
}
_lastTouchMovePoint = _point;
_lastTouchMoveEventTime = eventTime;
}
else if (e.Action == MotionEventActions.Down)
{
_lastTouchMovePoint = _point;
UIElement parent = root.InputManager.MouseDevice.Captured;
while (parent != null)
{
if (parent is IEditor)
{
break;
}
else
{
parent = parent.Parent;
}
}
if (parent is IEditor editor)
{
ShowKeyboard(!editor.IsReadOnly, editor);
}
else
{
ShowKeyboard(false, null);
}
}
else
{
_lastTouchMovePoint = null;
if (mouseEventType == EventType.MouseUp)
{
root.InputManager.MouseDevice.Capture(null);
}
}
if (mouseEventType == EventType.MouseUp && root.IsMouseOver)
{
root.InputManager.MouseDevice.ProcessEvent(new MouseEventArgs(root, false, false, false, new CPF.Drawing.Point(-1, -1), root.InputManager.MouseDevice, true), root.LayoutManager.VisibleUIElements, EventType.MouseLeave);
}
}
//else if (root.IsMouseOver)
//{
// root.InputManager.MouseDevice.ProcessEvent(new MouseEventArgs(root, false, false, false, new CPF.Drawing.Point(-1, -1), root.InputManager.MouseDevice), root.LayoutManager.VisibleUIElements, EventType.MouseLeave);
//}
}
return e.Action != MotionEventActions.Up;
}
//public override bool DispatchKeyEvent(KeyEvent e)
//{
// //var m = new WindowManagerLayoutParams(100, 100, 100, 100, WindowManagerTypes.Phone | WindowManagerTypes.ApplicationOverlay, WindowManagerFlags.NotFocusable, global::Android.Graphics.Format.Rgbx8888);
// ////m.Gravity = GravityFlags.Left | GravityFlags.Top;
// //activity.WindowManager.AddView(new TestView(activity) { Background = new global::Android.Graphics.Drawables.ColorDrawable(global::Android.Graphics.Color.Argb(100, 255, 0, 0)) }, m);
//}
public void DispatchSetActivated(bool activated)
{
if (activated)
{
(this as IViewImpl).Activated();
}
else
{
Deactivated();
}
}
public Screen Screen
{
get
{
var display = owner.Display;
if (display == null)
{
display = activity.WindowManager.DefaultDisplay;
//return new Screen(new Drawing.Rect(), new Drawing.Rect(), true);
}
//global::Android.Graphics.Point point = new global::Android.Graphics.Point();
//display.GetRealSize(point);
global::Android.Graphics.Rect rect = new global::Android.Graphics.Rect();
display.GetRectSize(rect);
global::Android.Graphics.Point point1 = new global::Android.Graphics.Point();
display.GetRealSize(point1);
var screen = new Screen(new Drawing.Rect(0, 0, point1.X, point1.Y), new Drawing.Rect(rect.Left, rect.Top, rect.Right, rect.Bottom), true);
return screen;
}
}
public float RenderScaling
{
get
{
return global::Android.App.Application.Context.Resources.DisplayMetrics.ScaledDensity;
}
}
public float LayoutScaling => RenderScaling;
public Action ScalingChanged { get; set; }
public Action<Size> Resized { get; set; }
public Action<PixelPoint> PositionChanged { get; set; }
public Action Activated { get; set; }
public Action Deactivated { get; set; }
public bool CanActivate { get { return owner.Focusable; } set { owner.Focusable = value; } }
public PixelPoint Position
{
get
{
if (owner.Parent is PopupImpl popup)
{
return popup.Location;
}
if (owner.Parent is ViewGroup group)
{
return new PixelPoint(group.Left, group.Top);
}
return new PixelPoint(owner.Left, owner.Top);
}
set
{
//Console.WriteLine(value);
if (owner.Parent is PopupImpl windowImpl)
{
windowImpl.UpdateLayout(value.X, value.Y, windowImpl.LayoutParameters.Width, windowImpl.LayoutParameters.Height);
}
else if (owner.Parent is ViewGroup group)
{
//var w = group.LayoutParameters.Width;
//var h = group.LayoutParameters.Height;
//group.Layout(value.X, value.Y, w, h);
group.Top = value.Y;
group.Left = value.X;
}
}
}
public void Activate()
{
//throw new NotImplementedException();
owner.RequestFocus();
}
public void Capture()
{
owner.RequestPointerCapture();
//throw new NotImplementedException();
}
public Drawing.Point PointToClient(Drawing.Point point)
{
var position = new int[] { 0, 0 };
owner.GetLocationOnScreen(position);
var p = new Drawing.Point(point.X - position[0], point.Y - position[1]);
return p / LayoutScaling;
}
public Drawing.Point PointToScreen(Drawing.Point point)
{
var p = point * LayoutScaling;
var position = new int[] { 0, 0 };
owner.GetLocationOnScreen(position);
return new Drawing.Point(position[0] + p.X, position[1] + p.Y);
}
public void ReleaseCapture()
{
owner.ReleasePointerCapture();
//throw new NotImplementedException();
}
public void SetCursor(Cursor cursor)
{
//throw new NotImplementedException();
}
//internal bool IMEEnable = true;
public void SetIMEEnable(bool enable)
{
//IMEEnable = enable;
}
bool showInput;
internal static IEditor editor;
public void ShowKeyboard(bool show, IEditor editor)
{
if (show == showInput && GeneralView.editor == editor)
{
return;
}
var input = owner.Context.GetSystemService(Context.InputMethodService).JavaCast<InputMethodManager>();
GeneralView.editor = editor;
if (show)
{
input.HideSoftInputFromWindow(owner.WindowToken, HideSoftInputFlags.None);
input.RestartInput(owner);
}
showInput = show;
if (show)
{
input.ShowSoftInput(owner, 0);
}
else
{
input.HideSoftInputFromWindow(owner.WindowToken, HideSoftInputFlags.None);
//IMEEnable = true;
}
}
void IViewImpl.SetIMEPosition(Drawing.Point point)
{
//throw new NotImplementedException();
}
public void SetRoot(Controls.View view)
{
root = view;
root.Background = CPF.Drawing.Color.White;
//root.Visibility = CPF.Visibility.Visible;
root.CanActivate = true;
root.LayoutUpdated += Root_LayoutUpdated;
scale = root.LayoutScaling;
}
public void SetVisible(bool visible)
{
if (owner.Parent == null)
{
return;
}
if (visible)
{
((ViewGroup)owner.Parent).Visibility = ViewStates.Visible;
}
else
{
((ViewGroup)owner.Parent).Visibility = ViewStates.Gone;
}
//throw new NotImplementedException();
}
public void Dispose()
{
if (owner != null)
{
var o = owner;
owner = null;
o.Dispose();
}
}
private static readonly Dictionary<Keycode, Keys> KeyDic = new Dictionary<Keycode, Keys>
{
// { Keycode.Cancel?, Key.Cancel },
{ Keycode.Del, Keys.Back },
{ Keycode.Tab, Keys.Tab },
// { Keycode.Linefeed?, Key.LineFeed },
{ Keycode.Clear, Keys.Clear },
{ Keycode.Enter, Keys.Return },
{ Keycode.MediaPause, Keys.Pause },
//{ Keycode.?, Key.CapsLock }
//{ Keycode.?, Key.HangulMode }
//{ Keycode.?, Key.JunjaMode }
//{ Keycode.?, Key.FinalMode }
//{ Keycode.?, Key.KanjiMode }
{ Keycode.Escape, Keys.Escape },
//{ Keycode.?, Key.ImeConvert }
//{ Keycode.?, Key.ImeNonConvert }
//{ Keycode.?, Key.ImeAccept }
//{ Keycode.?, Key.ImeModeChange }
{ Keycode.Space, Keys.Space },
{ Keycode.PageUp, Keys.Prior },
{ Keycode.PageDown, Keys.PageDown },
// { Keycode.end?, Key.End },
{ Keycode.Home, Keys.Home },
{ Keycode.DpadLeft, Keys.Left },
{ Keycode.DpadUp, Keys.Up },
{ Keycode.DpadRight, Keys.Right },
{ Keycode.DpadDown, Keys.Down },
// { Keycode.ButtonSelect?, Key.Select },
// { Keycode.print?, Key.Print },
//{ Keycode.execute?, Key.Execute },
// { Keycode.snap, Key.Snapshot }
{ Keycode.Insert, Keys.Insert },
{ Keycode.ForwardDel, Keys.Delete },
//{ Keycode.help, Key.Help },
//{ Keycode.?, Key.D0 }
//{ Keycode.?, Key.D1 }
//{ Keycode.?, Key.D2 }
//{ Keycode.?, Key.D3 }
//{ Keycode.?, Key.D4 }
//{ Keycode.?, Key.D5 }
//{ Keycode.?, Key.D6 }
//{ Keycode.?, Key.D7 }
//{ Keycode.?, Key.D8 }
//{ Keycode.?, Key.D9 }
{ Keycode.A, Keys.A },
{ Keycode.B, Keys.B },
{ Keycode.C, Keys.C },
{ Keycode.D, Keys.D },
{ Keycode.E, Keys.E },
{ Keycode.F, Keys.F },
{ Keycode.G, Keys.G },
{ Keycode.H, Keys.H },
{ Keycode.I, Keys.I },
{ Keycode.J, Keys.J },
{ Keycode.K, Keys.K },
{ Keycode.L, Keys.L },
{ Keycode.M, Keys.M },
{ Keycode.N, Keys.N },
{ Keycode.O, Keys.O },
{ Keycode.P, Keys.P },
{ Keycode.Q, Keys.Q },
{ Keycode.R, Keys.R },
{ Keycode.S, Keys.S },
{ Keycode.T, Keys.T },
{ Keycode.U, Keys.U },
{ Keycode.V, Keys.V },
{ Keycode.W, Keys.W },
{ Keycode.X, Keys.X },
{ Keycode.Y, Keys.Y },
{ Keycode.Z, Keys.Z },
//{ Keycode.a, Key.A },
//{ Keycode.b, Key.B },
//{ Keycode.c, Key.C },
//{ Keycode.d, Key.D },
//{ Keycode.e, Key.E },
//{ Keycode.f, Key.F },
//{ Keycode.g, Key.G },
//{ Keycode.h, Key.H },
//{ Keycode.i, Key.I },
//{ Keycode.j, Key.J },
//{ Keycode.k, Key.K },
//{ Keycode.l, Key.L },
//{ Keycode.m, Key.M },
//{ Keycode.n, Key.N },
//{ Keycode.o, Key.O },
//{ Keycode.p, Key.P },
//{ Keycode.q, Key.Q },
//{ Keycode.r, Key.R },
//{ Keycode.s, Key.S },
//{ Keycode.t, Key.T },
//{ Keycode.u, Key.U },
//{ Keycode.v, Key.V },
//{ Keycode.w, Key.W },
//{ Keycode.x, Key.X },
//{ Keycode.y, Key.Y },
//{ Keycode.z, Key.Z },
//{ Keycode.?, Key.LWin }
//{ Keycode.?, Key.RWin }
//{ Keycode.?, Key.Apps }
//{ Keycode.?, Key.Sleep }
//{ Keycode.?, Key.NumPad0 }
//{ Keycode.?, Key.NumPad1 }
//{ Keycode.?, Key.NumPad2 }
//{ Keycode.?, Key.NumPad3 }
//{ Keycode.?, Key.NumPad4 }
//{ Keycode.?, Key.NumPad5 }
//{ Keycode.?, Key.NumPad6 }
//{ Keycode.?, Key.NumPad7 }
//{ Keycode.?, Key.NumPad8 }
//{ Keycode.?, Key.NumPad9 }
{ Keycode.NumpadMultiply, Keys.Multiply },
{ Keycode.NumpadAdd, Keys.Add },
{ Keycode.NumpadComma, Keys.Separator },
{ Keycode.NumpadSubtract, Keys.Subtract },
//{ Keycode.numpaddecimal?, Key.Decimal }
{ Keycode.NumpadDivide, Keys.Divide },
{ Keycode.F1, Keys.F1 },
{ Keycode.F2, Keys.F2 },
{ Keycode.F3, Keys.F3 },
{ Keycode.F4, Keys.F4 },
{ Keycode.F5, Keys.F5 },
{ Keycode.F6, Keys.F6 },
{ Keycode.F7, Keys.F7 },
{ Keycode.F8, Keys.F8 },
{ Keycode.F9, Keys.F9 },
{ Keycode.F10, Keys.F10 },
{ Keycode.F11, Keys.F11 },
{ Keycode.F12, Keys.F12 },
//{ Keycode.f13, Key.F13 },
//{ Keycode.F14, Key.F14 },
//{ Keycode.L5, Key.F15 },
//{ Keycode.F16, Key.F16 },
//{ Keycode.F17, Key.F17 },
//{ Keycode.L8, Key.F18 },
//{ Keycode.L9, Key.F19 },
//{ Keycode.L10, Key.F20 },
//{ Keycode.R1, Key.F21 },
//{ Keycode.R2, Key.F22 },
//{ Keycode.F23, Key.F23 },
//{ Keycode.R4, Key.F24 },
// { Keycode.numpad, Key.NumLock }
{ Keycode.ScrollLock, Keys.Scroll },
{ Keycode.ShiftLeft, Keys.LeftShift },
//{ Keycode.?, Key.RightShift }
//{ Keycode.?, Key.LeftCtrl }
//{ Keycode.?, Key.RightCtrl }
//{ Keycode.?, Key.LeftAlt }
//{ Keycode.?, Key.RightAlt }
//{ Keycode.?, Key.BrowserBack }
//{ Keycode.?, Key.BrowserForward }
//{ Keycode.?, Key.BrowserRefresh }
//{ Keycode.?, Key.BrowserStop }
//{ Keycode.?, Key.BrowserSearch }
//{ Keycode.?, Key.BrowserFavorites }
//{ Keycode.?, Key.BrowserHome }
//{ Keycode.?, Key.VolumeMute }
//{ Keycode.?, Key.VolumeDown }
//{ Keycode.?, Key.VolumeUp }
//{ Keycode.?, Key.MediaNextTrack }
//{ Keycode.?, Key.MediaPreviousTrack }
//{ Keycode.?, Key.MediaStop }
//{ Keycode.?, Key.MediaPlayPause }
//{ Keycode.?, Key.LaunchMail }
//{ Keycode.?, Key.SelectMedia }
//{ Keycode.?, Key.LaunchApplication1 }
//{ Keycode.?, Key.LaunchApplication2 }
//{ Keycode.?, Key.OemSemicolon }
//{ Keycode.?, Key.OemPlus }
//{ Keycode.?, Key.OemComma }
//{ Keycode.?, Key.OemMinus }
//{ Keycode.?, Key.OemPeriod }
//{ Keycode.?, Key.Oem2 }
//{ Keycode.?, Key.OemTilde }
//{ Keycode.?, Key.AbntC1 }
//{ Keycode.?, Key.AbntC2 }
//{ Keycode.?, Key.Oem4 }
//{ Keycode.?, Key.OemPipe }
//{ Keycode.?, Key.OemCloseBrackets }
//{ Keycode.?, Key.Oem7 }
//{ Keycode.?, Key.Oem8 }
//{ Keycode.?, Key.Oem102 }
//{ Keycode.?, Key.ImeProcessed }
//{ Keycode.?, Key.System }
//{ Keycode.?, Key.OemAttn }
//{ Keycode.?, Key.OemFinish }
//{ Keycode.?, Key.DbeHiragana }
//{ Keycode.?, Key.OemAuto }
//{ Keycode.?, Key.DbeDbcsChar }
//{ Keycode.?, Key.OemBackTab }
//{ Keycode.?, Key.Attn }
//{ Keycode.?, Key.DbeEnterWordRegisterMode }
//{ Keycode.?, Key.DbeEnterImeConfigureMode }
//{ Keycode.?, Key.EraseEof }
//{ Keycode.?, Key.Play }
//{ Keycode.?, Key.Zoom }
//{ Keycode.?, Key.NoName }
//{ Keycode.?, Key.DbeEnterDialogConversionMode }
//{ Keycode.?, Key.OemClear }
//{ Keycode.?, Key.DeadCharProcessed }
};
internal static Keys ConvertKey(Keycode key)
{
Keys result;
return KeyDic.TryGetValue(key, out result) ? result : Keys.None;
}
private static InputModifiers GetModifierKeys(KeyEvent e)
{
var rv = InputModifiers.None;
if (e.IsCtrlPressed) rv |= InputModifiers.Control;
if (e.IsShiftPressed) rv |= InputModifiers.Shift;
return rv;
}
}
}

View File

@ -0,0 +1,31 @@
using Android.App;
using Android.Content;
using Android.Content.Res;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using CPF.Platform;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.Graphics;
namespace CPF.Android
{
interface ISurfaceView : IViewImpl, View.IOnTouchListener, View.IOnKeyListener
{
CpfView CpfView { get; }
Resources Resources { get; }
void GetWindowVisibleDisplayFrame(Rect outRect);
CPF.Controls.View Root { get; }
void OnPaint();
GeneralView GeneralView { get; }
}
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Views.InputMethods;
using Android.Widget;
using CPF.Input;
using Java.Lang;
namespace CPF.Android
{
class InputConnection : BaseInputConnection
{
public InputConnection(ISurfaceView view) : base(view as View, true)
{
this.view = view;
}
ISurfaceView view;
public override bool CommitText(ICharSequence text, int newCursorPosition)
{
view.Root.InputManager.KeyboardDevice.ProcessEvent(new TextInputEventArgs(view.Root, view.Root.InputManager.KeyboardDevice, text.ToString()), KeyEventType.TextInput);
return base.CommitText(text, newCursorPosition);
}
//public override bool SetComposingText(ICharSequence text, int newCursorPosition)
//{
// return base.SetComposingText(text, newCursorPosition);
//}
//public override bool FinishComposingText()
//{
// return false;
//}
//ExtractedText extractedText = new ExtractedText() { Text = CharSequence.ArrayFromStringArray(new string[] { "test" })[0] };
//public override ExtractedText GetExtractedText(ExtractedTextRequest request, [GeneratedEnum] GetTextFlags flags)
//{
// return extractedText;
//}
//public override ICharSequence GetTextAfterCursorFormatted(int length, [GeneratedEnum] GetTextFlags flags)
//{
// return CharSequence.ArrayFromStringArray(new string[] { "s" })[0];
//}
//public override ICharSequence GetTextBeforeCursorFormatted(int length, [GeneratedEnum] GetTextFlags flags)
//{
// return CharSequence.ArrayFromStringArray(new string[] { "t" })[0];
//}
}
}

View File

@ -0,0 +1,49 @@
using CPF;
using CPF.Animation;
using CPF.Controls;
using CPF.Drawing;
using CPF.Input;
using CPF.Shapes;
using CPF.Styling;
using System;
using System.Collections.Generic;
using System.Text;
namespace CPF.Android
{
public class ListBoxTemplate : ListBoxItem
{
protected override void InitializeComponent()
{
BorderThickness = "0,0,0,1";
BorderType = BorderType.BorderThickness;
BorderFill = "#B4B4B4";
Height = 37.8f;
Width = 245.7f;
//模板定义
Children.Add(new TextBlock
{
MarginLeft = 42.1f,
MarginRight = 13.7f,
Width = 190f,
Text = "CPF控件",
Bindings =
{
{nameof(TextBlock.Text),"Item1" }
}
});
Children.Add(new Picture
{
MarginLeft = 6.1f,
Width = 28.2f,
Height = 26.9f,
Bindings =
{
{nameof(Picture.Source),"Item2" }
}
});
Triggers.Add(nameof(IsSelected), Relation.Me, null, (nameof(Background), "#f00"));
}
}
}

117
CPF.Android/NativeImpl.cs Normal file
View File

@ -0,0 +1,117 @@
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Graphics.Drawables;
using Android.Widget;
using CPF.Drawing;
using CPF.Platform;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CPF.Controls;
namespace CPF.Android
{
class NativeImpl : AbsoluteLayout, INativeImpl
{
public NativeImpl() : base(CpfActivity.CurrentActivity)
{
//AddView(new global::Android.Widget.Button(CpfActivity.CurrentActivity) { Text= "原生控件" });
}
public void SetBackColor(Color color)
{
base.Background = new ColorDrawable(global::Android.Graphics.Color.Argb(color.A, color.R, color.G, color.B));
}
Rect clipRect;
bool isVisible;
int width;
int height;
int left;
int top;
public void SetBounds(Rect boundsRect, Rect clip, bool visible)
{
if (visible)
{
var l = (int)(boundsRect.Left * parent.RenderScaling + (parent as IViewImpl).Position.X);
var t = (int)(boundsRect.Top * parent.RenderScaling + (parent as IViewImpl).Position.Y);
var w = (int)(boundsRect.Width * parent.RenderScaling);
var h = (int)(boundsRect.Height * parent.RenderScaling);
//Layout(l, t, l + w, t + h);
if (isVisible != visible || clipRect != clip || top != t || l != left || height != h || width != w)
{
top = t;
left = l;
height = h;
width = w;
if (!(parent.GeneralView.softKeyboardListner._wasKeyboard && parent.GeneralView.softKeyboardListner.viewImpl != null))
{
this.Visibility = ViewStates.Visible;
var margin = new LayoutParams(w, h, l, t);
LayoutParameters = margin;
//parent.CpfView.UpdateViewLayout(this, margin);
if (!float.IsInfinity(clip.Width) && !float.IsInfinity(clip.Height) && clipRect != clip)
{
clipRect = clip;
base.ClipBounds = new global::Android.Graphics.Rect((int)(clip.X * parent.RenderScaling), (int)(clip.Y * parent.RenderScaling), (int)(clip.Right * parent.RenderScaling), (int)(clip.Bottom * parent.RenderScaling));
}
}
}
}
else
{
this.Visibility = ViewStates.Gone;
}
isVisible = visible;
}
object content;
public void SetContent(object content)
{
if (content is global::Android.Views.View view)
{
this.AddView(view);
}
else
{
if (this.content is global::Android.Views.View old)
{
this.RemoveView(old);
}
}
this.content = content;
}
public void SetOwner(NativeElement owner)
{
}
ISurfaceView parent;
object INativeImpl.Handle => this;
public void SetParent(IViewImpl parent)
{
if (parent != null)
{
if (parent is AndroidView popup)
{
popup.CpfView.AddView(this);
}
}
else
{
if (this.parent != null)
{
this.parent.CpfView.RemoveView(this);
}
}
this.parent = parent as ISurfaceView;
}
}
}

View File

@ -0,0 +1,174 @@
using CPF;
using CPF.Animation;
using CPF.Controls;
using CPF.Drawing;
using CPF.Shapes;
using CPF.Styling;
using System;
using System.Collections.Generic;
using System.Text;
using Java.IO;
using CPF.Input;
namespace CPF.Android
{
public class OpenFileDialogView : Control
{
protected override void InitializeComponent()
{
Background = "#FFFFFF";
Width = 400;
Height = 600;
if (!DesignMode)
{
Width = "90%";
Height = "90%";
}
Children.Add(new TextBlock
{
FontSize = 20f,
MarginTop = 19.3f,
Text = "文件选择"
});
Children.Add(new TextBlock
{
Height = 20f,
TextTrimming = TextTrimming.CharacterEllipsis,
MarginTop = 57.2f,
Width = "90%",
Background = "#0f0",
Bindings =
{
{nameof(TextBlock.Text),nameof(CurrentDirectory),this }
}
});
Children.Add(new ListBox
{
MarginBottom = 90f,
MarginTop = 80,
Width = "90%",
ItemTemplate = typeof(ListBoxTemplate),
Background = "#f00",
Bindings =
{
{nameof(ListBox.Items),nameof(Files),this }
},
Commands =
{
{nameof(ListBox.MouseDown),ListBoxMouseDown }
}
});
Children.Add(new Button
{
Name = "ok",
MarginRight = 30,
MarginBottom = 29f,
Height = 31.1f,
Width = 88.8f,
Content = "确定",
Commands =
{
{nameof(Button.Click),(s,e)=>{ } }
}
});
Children.Add(new Button
{
Name = "cancel",
MarginBottom = 29f,
MarginLeft = 30f,
Height = 31.1f,
Width = 88.8f,
Content = "取消",
Commands =
{
{nameof(Button.Click),(s,e)=>{ this.Dispose(); } }
}
});
}
public OpenFileDialogView()
{
CurrentDirectory = global::Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
SelectedFiles = new Collection<string>();
Files = new Collection<(string, Image, bool, bool, string)>();
}
protected override void OnInitialized()
{
base.OnInitialized();
RefreshList();
}
private void RefreshList()
{
var dir = new File(CurrentDirectory);
//var list = System.IO.Directory.GetFiles(CurrentDirectory);
//var dirs = System.IO.Directory.GetDirectories(CurrentDirectory);
File[] files = dir.ListFiles();
var fs = Files;
fs.Clear();
if (dir.Parent != null)
{
fs.Add(("<", null, true, true, dir.Parent));
}
if (files != null)
{
foreach (var item in files)
{
fs.Add((item.Name, null, false, item.IsDirectory, item.AbsolutePath));
}
}
}
public Collection<string> SelectedFiles
{
get { return GetValue<Collection<string>>(); }
set { SetValue(value); }
}
public string CurrentDirectory
{
get { return GetValue<string>(); }
set { SetValue(value); }
}
public Collection<(string, Image, bool, bool, string)> Files
{
get { return GetValue<Collection<(string, Image, bool, bool, string)>>(); }
set { SetValue(value); }
}
void ListBoxMouseDown(CpfObject sender, object e)
{
var listbox = sender as ListBox;
var listItem = listbox.IsInItem((e as RoutedEventArgs).OriginalSource as UIElement);
if (listItem == null)
{
return;
}
var item = ((string, Image, bool, bool, string))listItem.DataContext;
if (item.Item1 != null)
{
if (item.Item3)
{
var dir = new File(CurrentDirectory);
if (dir.Parent != null && dir.Parent != "/")
{
CurrentDirectory = dir.Parent;
}
RefreshList();
}
else
{
if (item.Item4)
{
CurrentDirectory = item.Item5;
RefreshList();
}
else
{
}
}
}
}
}
}

261
CPF.Android/OpenGLView.cs Normal file
View File

@ -0,0 +1,261 @@
using Android.App;
using Android.Content;
using Android.Opengl;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Views.InputMethods;
using Android.Widget;
using CPF.Drawing;
using CPF.OpenGL;
using CPF.Platform;
using Javax.Microedition.Khronos.Opengles;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace CPF.Android
{
class OpenGLView : GLSurfaceView, ISurfaceView, GLSurfaceView.IRenderer, IGlContext
{
public OpenGLView(Context context, UIElement content, CpfView cpfView) : base(context)
{
SetEGLContextClientVersion(2);
SetEGLConfigChooser(8, 8, 8, 8, 0, 8);
CpfView = cpfView;
generalView = new GeneralView(this, content);
cpfView.AddView(this);
generalView.Create();
SetRenderer(this);
}
IGlContext glContext;
GeneralView generalView;
public CpfView CpfView { get; }
public GeneralView GeneralView { get { return generalView; } }
public CPF.Controls.View Root
{
get { return generalView.Root; }
}
protected override int[] OnCreateDrawableState(int extraSpace)
{
generalView.OnCreateDrawableState(extraSpace);
return base.OnCreateDrawableState(extraSpace);
}
public override void Invalidate()
{
generalView.Invalidate();
base.Invalidate();
}
public override void Invalidate(global::Android.Graphics.Rect dirty)
{
Invalidate();
}
public override void Invalidate(int l, int t, int r, int b)
{
Invalidate();
}
//float scale;
protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
{
generalView.OnLayout(changed, left, top, right, bottom);
}
public void OnPaint()
{
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
var root = generalView.Root;
GetFramebufferInfo(out var fb, out var sam, out var sten);
using (DrawingContext dc = DrawingContext.FromRenderTarget(new OpenGlRenderTarget<object>(null, this, newSize.Width, newSize.Height, fb, sam, sten)))
{
root.Invoke(() =>
{
root.LayoutManager.ExecuteLayoutPass();
if (root.LayoutManager.VisibleUIElements != null)
{
root.RenderView(dc, new CPF.Drawing.Rect(0, 0, newSize.Width, newSize.Height));
}
});
}
System.Diagnostics.Debug.WriteLine(stopwatch.ElapsedMilliseconds);
}
public override IInputConnection OnCreateInputConnection(EditorInfo outAttrs)
{
return generalView.OnCreateInputConnection(outAttrs);
}
public bool OnKey(View v, [GeneratedEnum] Keycode keyCode, KeyEvent e)
{
generalView.OnKey(v, keyCode, e);
return false;
}
public bool OnTouch(View v, MotionEvent e)
{
return generalView.OnTouch(v, e);
}
protected override void DispatchSetActivated(bool activated)
{
base.DispatchSetActivated(activated);
generalView.DispatchSetActivated(activated);
}
public Screen Screen
{
get
{
return generalView.Screen;
}
}
public float RenderScaling
{
get
{
return generalView.RenderScaling;
}
}
public float LayoutScaling => RenderScaling;
public Action ScalingChanged { get => generalView.ScalingChanged; set => generalView.ScalingChanged = value; }
public Action<Size> Resized { get => generalView.Resized; set => generalView.Resized = value; }
Action<PixelPoint> IViewImpl.PositionChanged { get => generalView.PositionChanged; set => generalView.PositionChanged = value; }
Action IViewImpl.Activated { get => generalView.Activated; set => generalView.Activated = value; }
public Action Deactivated { get => generalView.Deactivated; set => generalView.Deactivated = value; }
bool IViewImpl.CanActivate { get => generalView.CanActivate; set => generalView.CanActivate = value; }
PixelPoint IViewImpl.Position
{
get
{
return generalView.Position;
}
set
{
generalView.Position = value;
}
}
public IDisposable GRContext { get; set; }
void IViewImpl.Activate()
{
generalView.Activate();
}
void IViewImpl.Capture()
{
generalView.Capture();
}
void IViewImpl.Invalidate(in Drawing.Rect rect)
{
Invalidate();
}
Drawing.Point IViewImpl.PointToClient(Drawing.Point point)
{
return generalView.PointToClient(point);
}
Drawing.Point IViewImpl.PointToScreen(Drawing.Point point)
{
return generalView.PointToScreen(point);
}
void IViewImpl.ReleaseCapture()
{
generalView.ReleaseCapture();
}
void IViewImpl.SetCursor(Cursor cursor)
{
generalView.SetCursor(cursor);
}
void IViewImpl.SetIMEEnable(bool enable)
{
generalView.SetIMEEnable(enable);
}
public void ShowKeyboard(bool show, IEditor editor)
{
generalView.ShowKeyboard(show, editor);
}
void IViewImpl.SetIMEPosition(Drawing.Point point)
{
//throw new NotImplementedException();
}
void IViewImpl.SetRoot(Controls.View view)
{
generalView.SetRoot(view);
}
void IViewImpl.SetVisible(bool visible)
{
generalView.SetVisible(visible);
}
protected override void Dispose(bool disposing)
{
glContext?.Dispose();
glContext = null;
base.Dispose(disposing);
}
public void OnDrawFrame(IGL10 gl)
{
OnPaint();
}
//private Size lastSize;
private PixelSize newSize;
public void OnSurfaceChanged(IGL10 gl, int width, int height)
{
newSize = new PixelSize(width, height);
}
public void OnSurfaceCreated(IGL10 gl, Javax.Microedition.Khronos.Egl.EGLConfig config)
{
}
public void GetFramebufferInfo(out int framebuffer, out int samples, out int stencil)
{
var buffer = new int[3];
GLES20.GlGetIntegerv(GLES20.GlFramebufferBinding, buffer, 0);
GLES20.GlGetIntegerv(GLES20.GlStencilBits, buffer, 1);
GLES20.GlGetIntegerv(GLES20.GlSamples, buffer, 2);
samples = buffer[2];
stencil = buffer[1];
framebuffer = buffer[0];
}
public IntPtr GetProcAddress(string name)
{
return eglGetProcAddress(name);
}
const string lib = "/system/lib/egl/libEGL_mali.so";
[DllImport(lib)]
public extern static IntPtr eglGetProcAddress(string procname);
}
}

112
CPF.Android/PopupImpl.cs Normal file
View File

@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using CPF.Drawing;
using CPF.Platform;
namespace CPF.Android
{
public class PopupImpl : CpfView, IPopupImpl
{
public PopupImpl() : base(CpfActivity.CurrentActivity, null)
{
WindowType = WindowManagerTypes.ApplicationSubPanel;
WindowFlags = WindowManagerFlags.NotFocusable;
}
public Screen Screen => androidView.Screen;
public float RenderScaling => androidView.RenderScaling;
public float LayoutScaling => androidView.LayoutScaling;
public Action ScalingChanged { get => androidView.ScalingChanged; set => androidView.ScalingChanged = value; }
public Action<Size> Resized { get => androidView.Resized; set => androidView.Resized = value; }
public Action<PixelPoint> PositionChanged { get => (androidView as IViewImpl).PositionChanged; set => (androidView as IViewImpl).PositionChanged = value; }
public Action Deactivated { get => androidView.Deactivated; set => androidView.Deactivated = value; }
public bool CanActivate { get => (androidView as IViewImpl).CanActivate; set => (androidView as IViewImpl).CanActivate = value; }
public PixelPoint Position { get => (androidView as IViewImpl).Position; set => (androidView as IViewImpl).Position = value; }
Action IViewImpl.Activated { get => (androidView as IViewImpl).Activated; set => (androidView as IViewImpl).Activated = value; }
public void Activate()
{
(androidView as IViewImpl).Activate();
}
public void Capture()
{
(androidView as IViewImpl).Capture();
}
public void Invalidate(in Rect rect)
{
(androidView as IViewImpl).Invalidate(rect);
}
public Point PointToClient(Point point)
{
return (androidView as IViewImpl).PointToClient(point);
}
public Point PointToScreen(Point point)
{
return (androidView as IViewImpl).PointToScreen(point);
}
public void ReleaseCapture()
{
(androidView as IViewImpl).ReleaseCapture();
}
public void SetCursor(Cursor cursor)
{
(androidView as IViewImpl).SetCursor(cursor);
}
public void SetIMEEnable(bool enable)
{
(androidView as IViewImpl).SetIMEEnable(enable);
}
public void SetIMEPosition(Point point)
{
(androidView as IViewImpl).SetIMEPosition(point);
}
public void SetRoot(Controls.View view)
{
(androidView as IViewImpl).SetRoot(view);
}
bool isVisible;
public virtual void SetVisible(bool visible)
{
if (visible)
{
Root.LayoutManager.ExecuteLayoutPass();
if (!isVisible)
{
var margin = new WindowManagerLayoutParams((int)(Root.ActualSize.Width * LayoutScaling), (int)(Root.ActualSize.Height * LayoutScaling), (int)(Root.ActualOffset.X * LayoutScaling), (int)(Root.ActualOffset.Y * LayoutScaling), WindowType, WindowFlags, global::Android.Graphics.Format.Rgbx8888);
margin.Gravity = GravityFlags.Left | GravityFlags.Top;
Location = new PixelPoint((int)(Root.ActualOffset.X * LayoutScaling), (int)(Root.ActualOffset.Y * LayoutScaling));
CpfActivity.CurrentActivity.WindowManager.AddView(this, margin);
}
}
else
{
if (isVisible)
{
CpfActivity.CurrentActivity.WindowManager.RemoveView(this);
}
}
isVisible = visible;
}
}
}

View File

@ -0,0 +1,26 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Android.App;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CPF.Android")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CPF.Android")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

821
CPF.Android/Resources/Resource.designer.cs generated Normal file
View File

@ -0,0 +1,821 @@
#pragma warning disable 1591
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[assembly: global::Android.Runtime.ResourceDesignerAttribute("CPF.Android.Resource", IsApplication=false)]
namespace CPF.Android
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")]
public partial class Resource
{
static Resource()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
public partial class Attribute
{
// aapt resource value: 0x7F010000
public static int alpha = 2130771968;
// aapt resource value: 0x7F010001
public static int coordinatorLayoutStyle = 2130771969;
// aapt resource value: 0x7F010002
public static int font = 2130771970;
// aapt resource value: 0x7F010003
public static int fontProviderAuthority = 2130771971;
// aapt resource value: 0x7F010004
public static int fontProviderCerts = 2130771972;
// aapt resource value: 0x7F010005
public static int fontProviderFetchStrategy = 2130771973;
// aapt resource value: 0x7F010006
public static int fontProviderFetchTimeout = 2130771974;
// aapt resource value: 0x7F010007
public static int fontProviderPackage = 2130771975;
// aapt resource value: 0x7F010008
public static int fontProviderQuery = 2130771976;
// aapt resource value: 0x7F010009
public static int fontStyle = 2130771977;
// aapt resource value: 0x7F01000A
public static int fontVariationSettings = 2130771978;
// aapt resource value: 0x7F01000B
public static int fontWeight = 2130771979;
// aapt resource value: 0x7F01000C
public static int keylines = 2130771980;
// aapt resource value: 0x7F01000D
public static int layout_anchor = 2130771981;
// aapt resource value: 0x7F01000E
public static int layout_anchorGravity = 2130771982;
// aapt resource value: 0x7F01000F
public static int layout_behavior = 2130771983;
// aapt resource value: 0x7F010010
public static int layout_dodgeInsetEdges = 2130771984;
// aapt resource value: 0x7F010011
public static int layout_insetEdge = 2130771985;
// aapt resource value: 0x7F010012
public static int layout_keyline = 2130771986;
// aapt resource value: 0x7F010013
public static int statusBarBackground = 2130771987;
// aapt resource value: 0x7F010014
public static int ttcIndex = 2130771988;
static Attribute()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Attribute()
{
}
}
public partial class Color
{
// aapt resource value: 0x7F020000
public static int browser_actions_bg_grey = 2130837504;
// aapt resource value: 0x7F020001
public static int browser_actions_divider_color = 2130837505;
// aapt resource value: 0x7F020002
public static int browser_actions_text_color = 2130837506;
// aapt resource value: 0x7F020003
public static int browser_actions_title_color = 2130837507;
// aapt resource value: 0x7F020004
public static int notification_action_color_filter = 2130837508;
// aapt resource value: 0x7F020005
public static int notification_icon_bg_color = 2130837509;
// aapt resource value: 0x7F020006
public static int ripple_material_light = 2130837510;
// aapt resource value: 0x7F020007
public static int secondary_text_default_material_light = 2130837511;
static Color()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Color()
{
}
}
public partial class Dimension
{
// aapt resource value: 0x7F030000
public static int browser_actions_context_menu_max_width = 2130903040;
// aapt resource value: 0x7F030001
public static int browser_actions_context_menu_min_padding = 2130903041;
// aapt resource value: 0x7F030002
public static int compat_button_inset_horizontal_material = 2130903042;
// aapt resource value: 0x7F030003
public static int compat_button_inset_vertical_material = 2130903043;
// aapt resource value: 0x7F030004
public static int compat_button_padding_horizontal_material = 2130903044;
// aapt resource value: 0x7F030005
public static int compat_button_padding_vertical_material = 2130903045;
// aapt resource value: 0x7F030006
public static int compat_control_corner_material = 2130903046;
// aapt resource value: 0x7F030007
public static int compat_notification_large_icon_max_height = 2130903047;
// aapt resource value: 0x7F030008
public static int compat_notification_large_icon_max_width = 2130903048;
// aapt resource value: 0x7F030009
public static int notification_action_icon_size = 2130903049;
// aapt resource value: 0x7F03000A
public static int notification_action_text_size = 2130903050;
// aapt resource value: 0x7F03000B
public static int notification_big_circle_margin = 2130903051;
// aapt resource value: 0x7F03000C
public static int notification_content_margin_start = 2130903052;
// aapt resource value: 0x7F03000D
public static int notification_large_icon_height = 2130903053;
// aapt resource value: 0x7F03000E
public static int notification_large_icon_width = 2130903054;
// aapt resource value: 0x7F03000F
public static int notification_main_column_padding_top = 2130903055;
// aapt resource value: 0x7F030010
public static int notification_media_narrow_margin = 2130903056;
// aapt resource value: 0x7F030011
public static int notification_right_icon_size = 2130903057;
// aapt resource value: 0x7F030012
public static int notification_right_side_padding_top = 2130903058;
// aapt resource value: 0x7F030013
public static int notification_small_icon_background_padding = 2130903059;
// aapt resource value: 0x7F030014
public static int notification_small_icon_size_as_large = 2130903060;
// aapt resource value: 0x7F030015
public static int notification_subtext_size = 2130903061;
// aapt resource value: 0x7F030016
public static int notification_top_pad = 2130903062;
// aapt resource value: 0x7F030017
public static int notification_top_pad_large_text = 2130903063;
static Dimension()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Dimension()
{
}
}
public partial class Drawable
{
// aapt resource value: 0x7F040000
public static int filedialog_folder_l = 2130968576;
// aapt resource value: 0x7F040001
public static int filedialog_folder_m = 2130968577;
// aapt resource value: 0x7F040002
public static int filedialog_folder_s = 2130968578;
// aapt resource value: 0x7F040003
public static int filedialog_folder_up_l = 2130968579;
// aapt resource value: 0x7F040004
public static int filedialog_folder_up_m = 2130968580;
// aapt resource value: 0x7F040005
public static int filedialog_folder_up_s = 2130968581;
// aapt resource value: 0x7F040006
public static int filedialog_root_l = 2130968582;
// aapt resource value: 0x7F040007
public static int filedialog_root_m = 2130968583;
// aapt resource value: 0x7F040008
public static int filedialog_root_s = 2130968584;
// aapt resource value: 0x7F040009
public static int ic_about = 2130968585;
// aapt resource value: 0x7F04000A
public static int ic_launcher = 2130968586;
// aapt resource value: 0x7F04000B
public static int notification_action_background = 2130968587;
// aapt resource value: 0x7F04000C
public static int notification_bg = 2130968588;
// aapt resource value: 0x7F04000D
public static int notification_bg_low = 2130968589;
// aapt resource value: 0x7F04000E
public static int notification_bg_low_normal = 2130968590;
// aapt resource value: 0x7F04000F
public static int notification_bg_low_pressed = 2130968591;
// aapt resource value: 0x7F040010
public static int notification_bg_normal = 2130968592;
// aapt resource value: 0x7F040011
public static int notification_bg_normal_pressed = 2130968593;
// aapt resource value: 0x7F040012
public static int notification_icon_background = 2130968594;
// aapt resource value: 0x7F040013
public static int notification_template_icon_bg = 2130968595;
// aapt resource value: 0x7F040014
public static int notification_template_icon_low_bg = 2130968596;
// aapt resource value: 0x7F040015
public static int notification_tile_bg = 2130968597;
// aapt resource value: 0x7F040016
public static int notify_panel_notification_icon_bg = 2130968598;
static Drawable()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Drawable()
{
}
}
public partial class Id
{
// aapt resource value: 0x7F050004
public static int actions = 2131034116;
// aapt resource value: 0x7F050000
public static int action_container = 2131034112;
// aapt resource value: 0x7F050001
public static int action_divider = 2131034113;
// aapt resource value: 0x7F050002
public static int action_image = 2131034114;
// aapt resource value: 0x7F050003
public static int action_text = 2131034115;
// aapt resource value: 0x7F050005
public static int all = 2131034117;
// aapt resource value: 0x7F050006
public static int async = 2131034118;
// aapt resource value: 0x7F050007
public static int blocking = 2131034119;
// aapt resource value: 0x7F050008
public static int bottom = 2131034120;
// aapt resource value: 0x7F050009
public static int browser_actions_header_text = 2131034121;
// aapt resource value: 0x7F05000C
public static int browser_actions_menu_items = 2131034124;
// aapt resource value: 0x7F05000A
public static int browser_actions_menu_item_icon = 2131034122;
// aapt resource value: 0x7F05000B
public static int browser_actions_menu_item_text = 2131034123;
// aapt resource value: 0x7F05000D
public static int browser_actions_menu_view = 2131034125;
// aapt resource value: 0x7F05000E
public static int center = 2131034126;
// aapt resource value: 0x7F05000F
public static int center_horizontal = 2131034127;
// aapt resource value: 0x7F050010
public static int center_vertical = 2131034128;
// aapt resource value: 0x7F050011
public static int chronometer = 2131034129;
// aapt resource value: 0x7F050012
public static int clip_horizontal = 2131034130;
// aapt resource value: 0x7F050013
public static int clip_vertical = 2131034131;
// aapt resource value: 0x7F050014
public static int end = 2131034132;
// aapt resource value: 0x7F050015
public static int fill = 2131034133;
// aapt resource value: 0x7F050016
public static int fill_horizontal = 2131034134;
// aapt resource value: 0x7F050017
public static int fill_vertical = 2131034135;
// aapt resource value: 0x7F050018
public static int forever = 2131034136;
// aapt resource value: 0x7F050019
public static int icon = 2131034137;
// aapt resource value: 0x7F05001A
public static int icon_group = 2131034138;
// aapt resource value: 0x7F05001B
public static int info = 2131034139;
// aapt resource value: 0x7F05001C
public static int italic = 2131034140;
// aapt resource value: 0x7F05001D
public static int left = 2131034141;
// aapt resource value: 0x7F05001E
public static int line1 = 2131034142;
// aapt resource value: 0x7F05001F
public static int line3 = 2131034143;
// aapt resource value: 0x7F050020
public static int none = 2131034144;
// aapt resource value: 0x7F050021
public static int normal = 2131034145;
// aapt resource value: 0x7F050022
public static int notification_background = 2131034146;
// aapt resource value: 0x7F050023
public static int notification_main_column = 2131034147;
// aapt resource value: 0x7F050024
public static int notification_main_column_container = 2131034148;
// aapt resource value: 0x7F050025
public static int right = 2131034149;
// aapt resource value: 0x7F050026
public static int right_icon = 2131034150;
// aapt resource value: 0x7F050027
public static int right_side = 2131034151;
// aapt resource value: 0x7F050028
public static int start = 2131034152;
// aapt resource value: 0x7F050029
public static int tag_transition_group = 2131034153;
// aapt resource value: 0x7F05002A
public static int tag_unhandled_key_event_manager = 2131034154;
// aapt resource value: 0x7F05002B
public static int tag_unhandled_key_listeners = 2131034155;
// aapt resource value: 0x7F05002C
public static int text = 2131034156;
// aapt resource value: 0x7F05002D
public static int text2 = 2131034157;
// aapt resource value: 0x7F05002E
public static int time = 2131034158;
// aapt resource value: 0x7F05002F
public static int title = 2131034159;
// aapt resource value: 0x7F050030
public static int top = 2131034160;
static Id()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Id()
{
}
}
public partial class Integer
{
// aapt resource value: 0x7F060000
public static int status_bar_notification_info_maxnum = 2131099648;
static Integer()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Integer()
{
}
}
public partial class Layout
{
// aapt resource value: 0x7F070000
public static int browser_actions_context_menu_page = 2131165184;
// aapt resource value: 0x7F070001
public static int browser_actions_context_menu_row = 2131165185;
// aapt resource value: 0x7F070002
public static int notification_action = 2131165186;
// aapt resource value: 0x7F070003
public static int notification_action_tombstone = 2131165187;
// aapt resource value: 0x7F070004
public static int notification_template_custom_big = 2131165188;
// aapt resource value: 0x7F070005
public static int notification_template_icon_group = 2131165189;
// aapt resource value: 0x7F070006
public static int notification_template_part_chronometer = 2131165190;
// aapt resource value: 0x7F070007
public static int notification_template_part_time = 2131165191;
static Layout()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Layout()
{
}
}
public partial class String
{
// aapt resource value: 0x7F080000
public static int action_settings = 2131230720;
// aapt resource value: 0x7F080001
public static int app_name = 2131230721;
// aapt resource value: 0x7F080002
public static int btn_cancel = 2131230722;
// aapt resource value: 0x7F080003
public static int btn_ok = 2131230723;
// aapt resource value: 0x7F080004
public static int hello_world = 2131230724;
// aapt resource value: 0x7F080005
public static int status_bar_notification_info_overflow = 2131230725;
// aapt resource value: 0x7F080006
public static int tag_fragment_FileSave = 2131230726;
// aapt resource value: 0x7F080007
public static int tag_fragment_FileSelect = 2131230727;
// aapt resource value: 0x7F080008
public static int tag_save_hint = 2131230728;
// aapt resource value: 0x7F080009
public static int tag_title_OpenFile = 2131230729;
// aapt resource value: 0x7F08000A
public static int tag_title_SaveFile = 2131230730;
static String()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private String()
{
}
}
public partial class Style
{
// aapt resource value: 0x7F090000
public static int TextAppearance_Compat_Notification = 2131296256;
// aapt resource value: 0x7F090001
public static int TextAppearance_Compat_Notification_Info = 2131296257;
// aapt resource value: 0x7F090002
public static int TextAppearance_Compat_Notification_Line2 = 2131296258;
// aapt resource value: 0x7F090003
public static int TextAppearance_Compat_Notification_Time = 2131296259;
// aapt resource value: 0x7F090004
public static int TextAppearance_Compat_Notification_Title = 2131296260;
// aapt resource value: 0x7F090005
public static int Widget_Compat_NotificationActionContainer = 2131296261;
// aapt resource value: 0x7F090006
public static int Widget_Compat_NotificationActionText = 2131296262;
// aapt resource value: 0x7F090007
public static int Widget_Support_CoordinatorLayout = 2131296263;
static Style()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Style()
{
}
}
public partial class Styleable
{
// aapt resource value: { 0x10101A5,0x101031F,0x7F010000 }
public static int[] ColorStateListItem = new int[] {
16843173,
16843551,
2130771968};
// aapt resource value: 2
public static int ColorStateListItem_alpha = 2;
// aapt resource value: 1
public static int ColorStateListItem_android_alpha = 1;
// aapt resource value: 0
public static int ColorStateListItem_android_color = 0;
// aapt resource value: { 0x7F01000C,0x7F010013 }
public static int[] CoordinatorLayout = new int[] {
2130771980,
2130771987};
// aapt resource value: 0
public static int CoordinatorLayout_keylines = 0;
// aapt resource value: { 0x10100B3,0x7F01000D,0x7F01000E,0x7F01000F,0x7F010010,0x7F010011,0x7F010012 }
public static int[] CoordinatorLayout_Layout = new int[] {
16842931,
2130771981,
2130771982,
2130771983,
2130771984,
2130771985,
2130771986};
// aapt resource value: 0
public static int CoordinatorLayout_Layout_android_layout_gravity = 0;
// aapt resource value: 1
public static int CoordinatorLayout_Layout_layout_anchor = 1;
// aapt resource value: 2
public static int CoordinatorLayout_Layout_layout_anchorGravity = 2;
// aapt resource value: 3
public static int CoordinatorLayout_Layout_layout_behavior = 3;
// aapt resource value: 4
public static int CoordinatorLayout_Layout_layout_dodgeInsetEdges = 4;
// aapt resource value: 5
public static int CoordinatorLayout_Layout_layout_insetEdge = 5;
// aapt resource value: 6
public static int CoordinatorLayout_Layout_layout_keyline = 6;
// aapt resource value: 1
public static int CoordinatorLayout_statusBarBackground = 1;
// aapt resource value: { 0x7F010003,0x7F010004,0x7F010005,0x7F010006,0x7F010007,0x7F010008 }
public static int[] FontFamily = new int[] {
2130771971,
2130771972,
2130771973,
2130771974,
2130771975,
2130771976};
// aapt resource value: { 0x1010532,0x1010533,0x101053F,0x101056F,0x1010570,0x7F010002,0x7F010009,0x7F01000A,0x7F01000B,0x7F010014 }
public static int[] FontFamilyFont = new int[] {
16844082,
16844083,
16844095,
16844143,
16844144,
2130771970,
2130771977,
2130771978,
2130771979,
2130771988};
// aapt resource value: 0
public static int FontFamilyFont_android_font = 0;
// aapt resource value: 2
public static int FontFamilyFont_android_fontStyle = 2;
// aapt resource value: 4
public static int FontFamilyFont_android_fontVariationSettings = 4;
// aapt resource value: 1
public static int FontFamilyFont_android_fontWeight = 1;
// aapt resource value: 3
public static int FontFamilyFont_android_ttcIndex = 3;
// aapt resource value: 5
public static int FontFamilyFont_font = 5;
// aapt resource value: 6
public static int FontFamilyFont_fontStyle = 6;
// aapt resource value: 7
public static int FontFamilyFont_fontVariationSettings = 7;
// aapt resource value: 8
public static int FontFamilyFont_fontWeight = 8;
// aapt resource value: 9
public static int FontFamilyFont_ttcIndex = 9;
// aapt resource value: 0
public static int FontFamily_fontProviderAuthority = 0;
// aapt resource value: 1
public static int FontFamily_fontProviderCerts = 1;
// aapt resource value: 2
public static int FontFamily_fontProviderFetchStrategy = 2;
// aapt resource value: 3
public static int FontFamily_fontProviderFetchTimeout = 3;
// aapt resource value: 4
public static int FontFamily_fontProviderPackage = 4;
// aapt resource value: 5
public static int FontFamily_fontProviderQuery = 5;
// aapt resource value: { 0x101019D,0x101019E,0x10101A1,0x10101A2,0x10101A3,0x10101A4,0x1010201,0x101020B,0x1010510,0x1010511,0x1010512,0x1010513 }
public static int[] GradientColor = new int[] {
16843165,
16843166,
16843169,
16843170,
16843171,
16843172,
16843265,
16843275,
16844048,
16844049,
16844050,
16844051};
// aapt resource value: { 0x10101A5,0x1010514 }
public static int[] GradientColorItem = new int[] {
16843173,
16844052};
// aapt resource value: 0
public static int GradientColorItem_android_color = 0;
// aapt resource value: 1
public static int GradientColorItem_android_offset = 1;
// aapt resource value: 7
public static int GradientColor_android_centerColor = 7;
// aapt resource value: 3
public static int GradientColor_android_centerX = 3;
// aapt resource value: 4
public static int GradientColor_android_centerY = 4;
// aapt resource value: 1
public static int GradientColor_android_endColor = 1;
// aapt resource value: 10
public static int GradientColor_android_endX = 10;
// aapt resource value: 11
public static int GradientColor_android_endY = 11;
// aapt resource value: 5
public static int GradientColor_android_gradientRadius = 5;
// aapt resource value: 0
public static int GradientColor_android_startColor = 0;
// aapt resource value: 8
public static int GradientColor_android_startX = 8;
// aapt resource value: 9
public static int GradientColor_android_startY = 9;
// aapt resource value: 6
public static int GradientColor_android_tileMode = 6;
// aapt resource value: 2
public static int GradientColor_android_type = 2;
static Styleable()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Styleable()
{
}
}
public partial class Xml
{
// aapt resource value: 0x7F0B0000
public static int xamarin_essentials_fileprovider_file_paths = 2131427328;
static Xml()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Xml()
{
}
}
}
}
#pragma warning restore 1591

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 943 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 849 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">ExmFileDialog</string>
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
<string name="btn_ok">确定</string>
<string name="btn_cancel">取消</string>
<string name="tag_title_OpenFile">打开文件</string>
<string name="tag_fragment_FileSelect">Open File</string>
<string name="tag_title_SaveFile">保存文件</string>
<string name="tag_fragment_FileSave">Save File</string>
<string name="tag_save_hint">请输入文件名</string>
</resources>

View File

@ -0,0 +1,70 @@
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using CPF.Platform;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CPF.Android
{
class SoftKeyboardListner : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
{
private const int DefaultKeyboardHeightDP = 100;
private static readonly int EstimatedKeyboardDP = DefaultKeyboardHeightDP + (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop ? 48 : 0);
private readonly ISurfaceView _host;
public bool _wasKeyboard;
public SoftKeyboardListner(View view)
{
_host = view as ISurfaceView;
}
public IViewImpl viewImpl = null;
int top = 0;
public void OnGlobalLayout()
{
int estimatedKeyboardHeight = (int)TypedValue.ApplyDimension(ComplexUnitType.Dip,
EstimatedKeyboardDP, _host.Resources.DisplayMetrics);
var rect = new global::Android.Graphics.Rect();
_host.GetWindowVisibleDisplayFrame(rect);
int heightDiff = (int)_host.Screen.Bounds.Height - (rect.Bottom);
var isKeyboard = heightDiff >= estimatedKeyboardHeight;
if (isKeyboard != _wasKeyboard)
{
if (GeneralView.editor != null && isKeyboard)
{
var ele = GeneralView.editor as UIElement;
var p = ele.PointToScreen(new Drawing.Point(ele.ActualSize.Width, ele.ActualSize.Height));
var offset = p.Y - (_host.Screen.Bounds.Height - heightDiff);
if (offset > 0)
{
viewImpl = ele.Root.ViewImpl;
top = viewImpl.Position.Y;
viewImpl.Position = new PixelPoint(viewImpl.Position.X, top - (int)offset);
}
}
else
{
//_host.Top = top;
if (viewImpl != null)
{
viewImpl.Position = new PixelPoint(viewImpl.Position.X, top);
viewImpl = null;
}
}
}
_wasKeyboard = isKeyboard;
}
}
}

175
CPF.Android/WindowImpl.cs Normal file
View File

@ -0,0 +1,175 @@
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using CPF.Controls;
using CPF.Drawing;
using CPF.Platform;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CPF.Android
{
public class WindowImpl : PopupImpl, IWindowImpl
{
public WindowImpl()
{
WindowType = WindowManagerTypes.DrawnApplication;
WindowFlags = WindowManagerFlags.LayoutNoLimits;
}
public Func<bool> Closing { get; set; }
public Action Closed { get; set; }
WindowState windowState;
public WindowState WindowState
{
get => windowState; set
{
if (this.LayoutParameters != null && Parent != null)
{
//if (windowState == WindowState.FullScreen)
//{
// //(Context as Activity).RequestWindowFeature(WindowFeatures.DefaultFeatures);
// (Context as Activity).Window.SetFlags(WindowManagerFlags.ForceNotFullscreen, WindowManagerFlags.ForceNotFullscreen);
//}
if (value == WindowState.Maximized || value == WindowState.FullScreen)
{
var size = Screen.WorkingArea.Size;
var main = (Context as CpfActivity).Main;
if (main != null)
{
size = new Size(main.Width, main.Height);
}
UpdateLayout(0, 0, (int)size.Width, (int)size.Height);
}
else if (value == WindowState.Normal)
{
if (normalRect.HasValue)
{
//Left = (int)normalRect.Value.Left;
//Top = (int)normalRect.Value.Top;
//LayoutParameters.Width = (int)normalRect.Value.Width;
//LayoutParameters.Height = (int)normalRect.Value.Height;
UpdateLayout((int)normalRect.Value.Left, (int)normalRect.Value.Top, (int)normalRect.Value.Width, (int)normalRect.Value.Height);
}
}
//else if (value == WindowState.FullScreen)
//{
// //(Context as Activity).RequestWindowFeature(WindowFeatures.NoTitle);//设置无标题
// (Context as Activity).Window.SetFlags(WindowManagerFlags.Fullscreen, WindowManagerFlags.Fullscreen);
// UpdateLayout(0, 0, (Context as Activity).Window.DecorView.Width, (Context as Activity).Window.DecorView.Height);
//}
}
this.Visibility = value == WindowState.Minimized ? ViewStates.Gone : ViewStates.Visible;
windowState = value;
}
}
protected override void OnVisibilityChanged(global::Android.Views.View changedView, [GeneratedEnum] ViewStates visibility)
{
base.OnVisibilityChanged(changedView, visibility);
windowState = visibility == ViewStates.Gone ? WindowState.Minimized : WindowState.Normal;
WindowStateChanged();
}
Rect? normalRect;
public override void SetVisible(bool visible)
{
//if ((windowState == WindowState.Maximized || windowState == WindowState.FullScreen) && visible)
//{
// Root.LayoutManager.ExecuteLayoutPass();
// var margin = new WindowManagerLayoutParams(WindowType, WindowFlags, global::Android.Graphics.Format.Rgbx8888);
// margin.Gravity = GravityFlags.Left | GravityFlags.Top;
// CpfActivity.CurrentActivity.WindowManager.AddView(this, margin);
//}
//else
//{
base.SetVisible(visible);
if (visible)
{
normalRect = new Rect(this.Left, this.Top, this.Width, this.Height);
}
//}
}
protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
{
base.OnLayout(changed, left, top, right, bottom);
if (Width != (Context as Activity).Window.DecorView.Width && Height != (Context as Activity).Window.DecorView.Height && Location.X != 0 && Location.Y != 0)
{
normalRect = new Rect(Location.X, Location.Y, Width, Height);
}
}
protected override void OnMove()
{
if (Width != (Context as Activity).Window.DecorView.Width && Height != (Context as Activity).Window.DecorView.Height && Location.X != 0 && Location.Y != 0)
{
normalRect = new Rect(Location.X, Location.Y, Width, Height);
}
}
public Action WindowStateChanged { get; set; }
public bool IsMain { get; set; }
public void Close()
{
if (Closing())
{
return;
}
base.SetVisible(false);
Closed();
}
public void SetFullscreen(bool fullscreen)
{
//if (fullscreen)
//{
// (Context as Activity).RequestWindowFeature(WindowFeatures.NoTitle);//设置无标题
// (Context as Activity).Window.SetFlags(WindowManagerFlags.Fullscreen, WindowManagerFlags.Fullscreen);//设置全屏
//}
//else
//{
// (Context as Activity).RequestWindowFeature(WindowFeatures.DefaultFeatures);
// (Context as Activity).Window.SetFlags(WindowManagerFlags.ForceNotFullscreen, WindowManagerFlags.ForceNotFullscreen);
//}
}
public void SetIcon(Image image)
{
}
public void SetTitle(string text)
{
}
public void ShowDialog(Controls.Window window)
{
//SetVisible(true);
Root.Visibility = CPF.Visibility.Visible;
}
public void ShowInTaskbar(bool value)
{
}
public void TopMost(bool value)
{
}
public void SetEnable(bool enable)
{
}
}
}

4
CPF.Android/license.txt Normal file
View File

@ -0,0 +1,4 @@
1、CPFCross platform UI framework版权归福州初心网络科技有限公司所有
2、未经书面同意不得向第三方泄露、给予或转让源码
3、可以修改二次开发用于一般商业软件开发。但是不允许作为控件库、UI框架类型的产品对外发布。
4、新增代码著作权归新增代码的作者所有。

View File

@ -0,0 +1,3 @@
手动修改 CPF.Android.nuspec 版本信息
修改CPF.Android里安卓SDK目录连接
在程序包管理器控制台输入 nuget pack CPF.Android

View File

@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Version>0.9.6.5</Version>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Authors>QQ:761716178</Authors>
<Company>QQ:761716178</Company>
<Product>QQ:761716178 跨平台UI框架</Product>
<Description>CPF(Cross platform UI framework) QQ:761716178 跨平台UI框架 http://cpf.cskin.net/</Description>
<Copyright>Copyright (c) 2020 by http://cpf.cskin.net/</Copyright>
<PackageReleaseNotes>CPF(Cross platform UI framework) QQ:761716178 跨平台UI框架 http://cpf.cskin.net/</PackageReleaseNotes>
<PackageId>Xhm.CPF.Linux</PackageId>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>D:\xhm\Documents\Visual Studio 2019\ConsoleApp1\CPF.Linux\CPF.Linux.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="1.68.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CPF\CPF.csproj" />
</ItemGroup>
</Project>

58
CPF.Linux/CPF.Linux.xml Normal file
View File

@ -0,0 +1,58 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>CPF.Linux</name>
</assembly>
<members>
<member name="P:CPF.Linux.LinuxPlatform.EnabledTouch">
<summary>
启用触摸事件之所有加上这个因为部分Linux对XI2支持有问题
</summary>
</member>
<member name="T:CPF.Linux.XEventHandler">
<summary>
处理事件返回值为true的时候不调用默认处理方法
</summary>
<param name="xEvent"></param>
<returns></returns>
</member>
<member name="M:CPF.Linux.CancelHandle.SetResult(System.Object)">
<summary>
设置数据同时设置Cancel=true
</summary>
<param name="data"></param>
</member>
<member name="M:CPF.Linux.LinuxSynchronizationContext.Post(System.Threading.SendOrPostCallback,System.Object)">
<summary>
异步
</summary>
<param name="d"></param>
<param name="state"></param>
</member>
<member name="F:CPF.Linux.XIMValuesList.count_values">
unsigned short
</member>
<member name="F:CPF.Linux.XIMValuesList.supported_values">
char**
</member>
<member name="F:CPF.Linux.XEventName.Expose">
<summary>
绘制
</summary>
</member>
<member name="M:CPF.Linux.X11Window.OnPropertyChange(System.IntPtr,System.Boolean,System.Nullable{CPF.Controls.WindowState})">
<summary>
</summary>
<param name="atom"></param>
<param name="hasValue"></param>
<param name="windowState">部分Linux里会丢失状态消息</param>
</member>
<member name="F:CPF.Linux.XrmValue.size">
unsigned int
</member>
<member name="F:CPF.Linux.XrmValue.addr">
XPointer->char*
</member>
</members>
</doc>

966
CPF.Linux/DataObject.cs Normal file
View File

@ -0,0 +1,966 @@
using System;
using System.Collections.Generic;
using System.Text;
using CPF.Input;
using System.Linq;
using System.Runtime.InteropServices;
using CPF.Drawing;
namespace CPF.Linux
{
class DataObject : IDataObject
{
public static IntPtr textAtom = XLib.XInternAtom(LinuxPlatform.Platform.Display, "text/plain", false);
public static IntPtr textUtf8Atom = XLib.XInternAtom(LinuxPlatform.Platform.Display, "text/plain;charset=utf-8", false);
public static IntPtr htmlAtom = XLib.XInternAtom(LinuxPlatform.Platform.Display, "text/html", false);
public static IntPtr fileNamesAtom = XLib.XInternAtom(LinuxPlatform.Platform.Display, "text/uri-list", false);
public static List<(IntPtr, IntPtr, DataFormat)> atomNonProtocols = new List<(IntPtr, IntPtr, DataFormat)>();
static DataObject()
{
var atoms = LinuxPlatform.Platform.Info.Atoms;
//atomNonProtocols.Add((textAtom, XLib.XInternAtom(LinuxPlatform.Platform.Display,
// String.Concat("MWFNonP+", "text/plain"), false), DataFormat.Text));
//atomNonProtocols.Add((htmlAtom, XLib.XInternAtom(LinuxPlatform.Platform.Display,
// String.Concat("MWFNonP+", "text/html"), false), DataFormat.Html));
//atomNonProtocols.Add((fileNamesAtom, XLib.XInternAtom(LinuxPlatform.Platform.Display,
// String.Concat("MWFNonP+", "text/uri-list"), false), DataFormat.FileNames));
atomNonProtocols.Add((atoms.UTF8_STRING, atoms.UTF8_STRING, DataFormat.Text));
atomNonProtocols.Add((atoms.COMPOUND_TEXT, atoms.COMPOUND_TEXT, DataFormat.Text));
atomNonProtocols.Add((atoms.XA_STRING, atoms.XA_STRING, DataFormat.Text));
atomNonProtocols.Add((atoms.STRING, atoms.STRING, DataFormat.Text));
atomNonProtocols.Add((atoms.TEXT, atoms.TEXT, DataFormat.Text));
}
string text;
string html;
List<string> fileNames;
X11Info x11Info;
public DataObject()
{
x11Info = LinuxPlatform.Platform.Info;
}
public bool Contains(DataFormat dataFormat)
{
switch (dataFormat)
{
case DataFormat.Text:
return text != null;
case DataFormat.Html:
return html != null;
case DataFormat.FileNames:
return fileNames != null;
default:
return false;
}
}
public object GetData(DataFormat dataFormat)
{
switch (dataFormat)
{
case DataFormat.Text:
return text;
case DataFormat.Html:
return html;
case DataFormat.FileNames:
return fileNames;
default:
return null;
}
}
public DragDropEffects EffectFromAction(IntPtr action)
{
if (action == x11Info.Atoms.XdndActionCopy)
return DragDropEffects.Copy;
else if (action == x11Info.Atoms.XdndActionMove)
return DragDropEffects.Move;
if (action == x11Info.Atoms.XdndActionLink)
return DragDropEffects.Link;
return DragDropEffects.None;
}
private IntPtr ActionFromEffect(DragDropEffects effect)
{
IntPtr action = IntPtr.Zero;
// We can't OR together actions on XDND so sadly the primary
// is the only one shown here
if ((effect & DragDropEffects.Copy) != 0)
action = x11Info.Atoms.XdndActionCopy;
else if ((effect & DragDropEffects.Move) != 0)
action = x11Info.Atoms.XdndActionMove;
else if ((effect & DragDropEffects.Link) != 0)
action = x11Info.Atoms.XdndActionLink;
return action;
}
//private IntPtr target;
//private IntPtr source;
//private IntPtr toplevel;
IntPtr[] SupportedTypes;
Point point;
CancelHandle cancelHandle;
IntPtr LastWindow;
IntPtr TargetWindow;
IntPtr SourceWindow;
DragState State;
IntPtr Action;
public DragDropEffects AllowedEffects;
//private bool dropped = false;
(DataFormat, object)[] data;
public DragDropEffects StartDrag(DragDropEffects allowedEffects, params (DataFormat, object)[] data)
{
if (X11Window.mouseDownWindow == IntPtr.Zero)
{
throw new Exception("必须是鼠标按下的时候调用");
}
SourceWindow = X11Window.mouseDownWindow;
State = DragState.Beginning;
//MouseState = XplatUIX11.MouseState;
AllowedEffects = allowedEffects;
Action = ActionFromEffect(allowedEffects);
this.data = data;
var suc = XLib.XSetSelectionOwner(LinuxPlatform.Platform.Display, x11Info.Atoms.XdndSelection, X11Window.mouseDownWindow, IntPtr.Zero);
List<IntPtr> types = new List<IntPtr>();
foreach (var item in data)
{
switch (item.Item1)
{
case DataFormat.Text:
types.Add(textAtom);
types.Add(x11Info.Atoms.UTF8_STRING);
types.Add(x11Info.Atoms.STRING);
types.Add(x11Info.Atoms.TEXT);
types.Add(x11Info.Atoms.COMPOUND_TEXT);
types.Add(textUtf8Atom);
break;
case DataFormat.Html:
types.Add(htmlAtom);
break;
case DataFormat.FileNames:
types.Add(fileNamesAtom);
break;
default:
break;
}
}
SupportedTypes = types.ToArray();
if (suc == 0)
{
Console.Error.WriteLine("Could not take ownership of XdndSelection aborting drag.");
//drag_data.Reset();
return DragDropEffects.None;
}
//source = toplevel = target = X11Window.mouseDownWindow;
State = DragState.Dragging;
//dropped = false;
TargetWindow = X11Window.mouseDownWindow;
SendEnter(X11Window.mouseDownWindow, X11Window.mouseDownWindow, SupportedTypes);
if (cancelHandle != null)
{
cancelHandle.Cancel = true;
}
cancelHandle = new CancelHandle();
LinuxPlatform.Platform.RunMainLoop(cancelHandle, OnEvent);
//if (!dropped)
return EffectFromAction(Action);
}
bool WillAccept;
// This version seems to be the most common
private static readonly IntPtr[] XdndVersion = new IntPtr[] { new IntPtr(4) };
private unsafe void SendEnter(IntPtr handle, IntPtr from, IntPtr[] supported)
{
XEvent xevent = new XEvent();
xevent.AnyEvent.type = XEventName.ClientMessage;
xevent.AnyEvent.display = LinuxPlatform.Platform.Display;
xevent.ClientMessageEvent.window = handle;
xevent.ClientMessageEvent.message_type = x11Info.Atoms.XdndEnter;
xevent.ClientMessageEvent.format = 32;
xevent.ClientMessageEvent.ptr1 = from;
xevent.ClientMessageEvent.ptr5 = Action;
// (int) xevent.ClientMessageEvent.ptr2 & 0x1)
// int ptr2 = 0x1;
// xevent.ClientMessageEvent.ptr2 = (IntPtr) ptr2;
// (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~(0xFF << 24)) | ((v) << 24)
//xevent.ClientMessageEvent.ptr2 = (IntPtr)((long)XdndVersion[0] << 24);
xevent.ClientMessageEvent.ptr2 = (IntPtr)67108865;
//if (supported.Length > 0)
// xevent.ClientMessageEvent.ptr3 = supported[0];
//if (supported.Length > 1)
// xevent.ClientMessageEvent.ptr4 = supported[1];
//if (supported.Length > 2)
// xevent.ClientMessageEvent.ptr5 = supported[2];
//var list = supported.Select(a => (int)a).ToArray();
//fixed (void* data = list)
//{
XLib.XChangeProperty(x11Info.Display, from, x11Info.Atoms.XdndTypeList, (IntPtr)Atom.XA_ATOM, 32, PropertyMode.Replace, supported, supported.Length);
//}
Console.WriteLine("SendEnter" + handle);
XLib.XSendEvent(LinuxPlatform.Platform.Display, handle, false, IntPtr.Zero, ref xevent);
}
bool OnEvent(ref XEvent xEvent)
{
//Console.WriteLine(xEvent.type);
switch (xEvent.type)
{
case XEventName.ButtonRelease:
SetResult();
return true;
case XEventName.MotionNotify:
point = new Point(xEvent.MotionEvent.x, xEvent.MotionEvent.y);
HandleMouseOver();
return true;
case XEventName.GenericEvent:
unsafe
{
fixed (void* data = &xEvent.GenericEventCookie)
{
XLib.XGetEventData(LinuxPlatform.Platform.Info.Display, data);
try
{
if (LinuxPlatform.Platform.Info.XInputOpcode ==
xEvent.GenericEventCookie.extension)
{
var xev = (XIEvent*)xEvent.GenericEventCookie.data;
if (LinuxPlatform.Platform.windows.TryGetValue(((XIDeviceEvent*)xev)->EventWindow, out var window))
{
if (xev->evtype == XiEventType.XI_ButtonRelease)
{
SetResult();
}
else if (xev->evtype == XiEventType.XI_Motion)
{
var ev = (XIDeviceEvent*)xev;
point = new Point((float)ev->event_x, (float)ev->event_y);
HandleMouseOver();
}
}
}
}
finally
{
if (xEvent.GenericEventCookie.data != null)
XLib.XFreeEventData(LinuxPlatform.Platform.Info.Display, data);
}
}
return true;
}
case XEventName.ClientMessage:
if (xEvent.ClientMessageEvent.message_type == x11Info.Atoms.XdndStatus)
{
WillAccept = ((int)xEvent.ClientMessageEvent.ptr2 & 0x1) != 0;
GiveFeedback(xEvent.ClientMessageEvent.ptr5);
//Console.WriteLine("WillAccept" + WillAccept);
return true;
}
else if (xEvent.ClientMessageEvent.message_type == x11Info.Atoms.XdndFinished)
{
cancelHandle.Cancel = true;
return true;
}
break;
case XEventName.SelectionRequest:
if (xEvent.SelectionRequestEvent.selection != x11Info.Atoms.XdndSelection)
break;
//Console.WriteLine("requestor:" + xEvent.SelectionRequestEvent.requestor);
//foreach (var item in data)
//{
var type = xEvent.SelectionRequestEvent.target;
var item = data.FirstOrDefault();
if (type == textAtom || type == textUtf8Atom || type == x11Info.Atoms.STRING || type == x11Info.Atoms.TEXT)
{
item = data.FirstOrDefault(a => a.Item1 == DataFormat.Text);
}
else if (type == htmlAtom)
{
item = data.FirstOrDefault(a => a.Item1 == DataFormat.Html);
}
else if (type == fileNamesAtom)
{
item = data.FirstOrDefault(a => a.Item1 == DataFormat.FileNames);
}
if (item.Item2 != null)
{
switch (item.Item1)
{
case DataFormat.Text:
SetTextData(item.Item2, ref xEvent);
break;
case DataFormat.Html:
SetHtmlData(item.Item2, ref xEvent);
break;
case DataFormat.FileNames:
SetFileNameData(item.Item2, ref xEvent);
break;
}
//}
XLib.XFlush(x11Info.Display);
}
return true;
}
return false;
}
private void SetResult()
{
if (State == DragState.Beginning)
{
//state = State.Accepting;
}
else if (State != DragState.None)
{
if (WillAccept)
{
SendDrop(TargetWindow, SourceWindow, IntPtr.Zero);
SendLeave(TargetWindow, SourceWindow);
XLib.XChangeActivePointerGrab(x11Info.Display,
EventMask.ButtonMotionMask |
EventMask.PointerMotionMask |
EventMask.ButtonPressMask |
EventMask.ButtonReleaseMask,
(IntPtr)((Cursor)Cursors.Arrow).PlatformCursor, IntPtr.Zero);
//if (QueryContinue(false, DragAction.Drop))
// return;
}
else
{
cancelHandle.Cancel = true;
SendLeave(TargetWindow, SourceWindow);
//if (QueryContinue(false, DragAction.Cancel))
// return;
// fallback if no movement was detected, as .net does.
//if (motion_poll == -1)
// DefaultEnterLeave(drag_data.Data);
}
State = DragState.None;
// WE can't reset the drag data yet as it is still
// most likely going to be used by the SelectionRequest
// handlers
}
}
DataFormat dataFormat;
DragDropEffects dragDropEffects;
public DragDropEffects DropEffects
{
get { return dragDropEffects; }
}
public void SetDragDropEffects(DragDropEffects dragDropEffects)
{
if (dragDropEffects != this.dragDropEffects)
{
SendStatus(TargetWindow, dragDropEffects);//发送消息,确定可以接受数据
}
this.dragDropEffects = dragDropEffects;
}
Threading.DispatcherTimer timer;
public void StopTimer()
{
if (timer != null)
{
timer.Stop();
timer.Dispose();
timer = null;
}
if (cancelHandle != null)
{
cancelHandle.Cancel = true;
cancelHandle = null;
}
}
public void DragEnter(ref XEvent xEvent, EventHandler eventHandler)
{
dataFormat = DataFormat.Unknown;
text = null;
html = null;
fileNames = null;
TargetWindow = xEvent.AnyEvent.window;
SourceWindow = xEvent.ClientMessageEvent.ptr1;
SendStatus(SourceWindow, DropEffects);//发送消息,确定可以接受数据
//确认可以接受数据的格式
foreach (IntPtr atom in SourceSupportedList(ref xEvent))
{
//Console.WriteLine("格式:" + XLib.GetAtomName(x11Info.Display, atom));
var f = atomNonProtocols.FirstOrDefault(a => a.Item1 == atom);
Console.WriteLine(XLib.GetAtomName(LinuxPlatform.Platform.Display, atom));
if (f.Item1 == IntPtr.Zero || dataFormat.HasFlag(f.Item3))
{
continue;
}
dataFormat |= f.Item3;
XLib.XConvertSelection(x11Info.Display, x11Info.Atoms.XdndSelection, atom,
f.Item2, TargetWindow, IntPtr.Zero /* CurrentTime */);
}
if (cancelHandle != null)
{
cancelHandle.Cancel = true;
}
cancelHandle = new CancelHandle();
LinuxPlatform.Platform.RunMainLoop(cancelHandle, OnAcceptEvent);
if (cancelHandle != null)
{
if (timer == null)
{
timer = new Threading.DispatcherTimer { Interval = TimeSpan.FromMilliseconds(100) };
timer.Tick += eventHandler;
}
timer.Start();
}
}
public bool OnAcceptEvent(ref XEvent xEvent)
{
if (xEvent.type == XEventName.SelectionNotify)
{
var df = dataFormat;
while (true)
{
if (df == DataFormat.Unknown)
{
break;
}
if (df.HasFlag(DataFormat.Text))
{
df ^= DataFormat.Text;
text = GetText(ref xEvent);
}
if (df.HasFlag(DataFormat.Html))
{
df ^= DataFormat.Html;
html = GetText(ref xEvent);
}
if (df.HasFlag(DataFormat.FileNames))
{
if (text == null)
{
text = GetText(ref xEvent);
}
df ^= DataFormat.FileNames;
List<string> uri_list = new List<string>();
string[] lines = text.Split(new char[] { '\r', '\n' });
foreach (string line in lines)
{
// # is a comment line (see RFC 2483)
if (line.StartsWith("#"))
continue;
try
{
Uri uri = new Uri(line);
uri_list.Add(uri.LocalPath);
}
catch { }
}
fileNames = uri_list;
//string[] l = (string[])uri_list.ToArray(typeof(string));
}
}
if (cancelHandle != null)
{
cancelHandle.Cancel = true;
}
return true;
}
return false;
}
public unsafe string GetText(ref XEvent xevent)
{
int nread = 0;
IntPtr nitems;
IntPtr bytes_after;
StringBuilder builder = new StringBuilder();
do
{
IntPtr actual_type;
int actual_fmt;
IntPtr data = IntPtr.Zero;
if (0 != XLib.XGetWindowProperty(x11Info.Display,
xevent.AnyEvent.window,
(IntPtr)xevent.SelectionEvent.property,
IntPtr.Zero, new IntPtr(0xffffff), false,
(IntPtr)Atom.AnyPropertyType, out actual_type,
out actual_fmt, out nitems, out bytes_after,
out data))
{
XLib.XFree(data);
break;
}
//if (unicode)
// builder.Append(Marshal.PtrToStringUni(data));
//else
// builder.Append(Marshal.PtrToStringAnsi(data));
Encoding textEnc = GetStringEncoding(xevent.SelectionEvent.property);
if (textEnc != null)
{
var text = textEnc.GetString((byte*)data.ToPointer(), nitems.ToInt32());
builder.Append(text);
}
else
{
builder.Append(Marshal.PtrToStringAnsi(data));
}
nread += nitems.ToInt32();
XLib.XFree(data);
} while (bytes_after.ToInt32() > 0);
if (nread == 0)
return null;
return builder.ToString();
}
Encoding GetStringEncoding(IntPtr atom)
{
return (atom == x11Info.Atoms.XA_STRING || atom == x11Info.Atoms.OEMTEXT)
? Encoding.ASCII
: (atom == x11Info.Atoms.UTF8_STRING || atom == textUtf8Atom)
? Encoding.UTF8
: (atom == x11Info.Atoms.UTF16_STRING || atom == x11Info.Atoms.UNICODETEXT || atomNonProtocols.First(a => a.Item1 == htmlAtom).Item2 == atom)
? Encoding.Unicode
: null;
}
public void SendStatus(IntPtr source, DragDropEffects effect)
{
XEvent xevent = new XEvent();
xevent.AnyEvent.type = XEventName.ClientMessage;
xevent.AnyEvent.display = x11Info.Display;
xevent.ClientMessageEvent.window = source;
xevent.ClientMessageEvent.message_type = x11Info.Atoms.XdndStatus;
xevent.ClientMessageEvent.format = 32;
xevent.ClientMessageEvent.ptr1 = TargetWindow;
if (effect != DragDropEffects.None)
xevent.ClientMessageEvent.ptr2 = (IntPtr)1;
xevent.ClientMessageEvent.ptr5 = ActionFromEffect(effect);
XLib.XSendEvent(x11Info.Display, source, false, IntPtr.Zero, ref xevent);
//XLib.XSync(x11Info.Display,false);
Console.WriteLine("state:" + effect);
}
private void GiveFeedback(IntPtr action)
{
//GiveFeedbackEventArgs gfe = new GiveFeedbackEventArgs(EffectFromAction(drag_data.Action), true);
//Control c = MwfWindow(source);
//c.DndFeedback(gfe);
//if (gfe.UseDefaultCursors)
{
Cursor cursor = Cursors.No;
if (WillAccept)
{
// Same order as on MS
if (action == x11Info.Atoms.XdndActionCopy)
cursor = Cursors.DragCopy;
else if (action == x11Info.Atoms.XdndActionLink)
cursor = Cursors.DragLink;
else if (action == x11Info.Atoms.XdndActionMove)
cursor = Cursors.DragMove;
}
// TODO: Try not to set the cursor so much
//if (cursor.Handle != CurrentCursorHandle) {
XLib.XChangeActivePointerGrab(x11Info.Display,
EventMask.ButtonMotionMask |
EventMask.PointerMotionMask |
EventMask.ButtonPressMask |
EventMask.ButtonReleaseMask,
(IntPtr)cursor.PlatformCursor, IntPtr.Zero);
//CurrentCursorHandle = cursor.Handle;
//}
}
}
public IntPtr[] SourceSupportedList(ref XEvent xevent)
{
IntPtr[] res;
if (((int)xevent.ClientMessageEvent.ptr2 & 0x1) == 0)
{
res = new IntPtr[3];
res[0] = xevent.ClientMessageEvent.ptr3;
res[1] = xevent.ClientMessageEvent.ptr4;
res[2] = xevent.ClientMessageEvent.ptr5;
}
else
{
IntPtr type;
int format;
IntPtr count;
IntPtr remaining;
IntPtr data;
XLib.XGetWindowProperty(x11Info.Display, SourceWindow, x11Info.Atoms.XdndTypeList,
IntPtr.Zero, new IntPtr(32), false, (IntPtr)Atom.XA_ATOM,
out type, out format, out count,
out remaining, out data);
res = new IntPtr[count.ToInt32()];
for (int i = 0; i < count.ToInt32(); i++)
{
res[i] = (IntPtr)Marshal.ReadInt32(data, i *
Marshal.SizeOf(typeof(IntPtr)));
}
XLib.XFree(data);
}
return res;
}
public void SetTextData(object data, ref XEvent xevent)
{
IntPtr buffer;
int len;
string str = data as string;
//if (str == null)
//{
// IDataObject dobj = data as IDataObject;
// if (dobj == null)
// return;
// str = (string)dobj.GetData("System.String", true);
//}
if (xevent.SelectionRequestEvent.target == (IntPtr)Atom.XA_STRING)
{
byte[] bytes = Encoding.ASCII.GetBytes(str);
buffer = Marshal.AllocHGlobal(bytes.Length);
len = bytes.Length;
for (int i = 0; i < len; i++)
Marshal.WriteByte(buffer, i, bytes[i]);
}
else if (xevent.SelectionRequestEvent.target == x11Info.Atoms.UTF8_STRING || xevent.SelectionRequestEvent.target == textUtf8Atom)
{
byte[] bytes = Encoding.UTF8.GetBytes(str);
buffer = Marshal.AllocHGlobal(bytes.Length);
len = bytes.Length;
for (int i = 0; i < len; i++)
Marshal.WriteByte(buffer, i, bytes[i]);
}
else
{
buffer = Marshal.StringToHGlobalAnsi(str);
len = 0;
while (Marshal.ReadByte(buffer, len) != 0)
len++;
}
SetProperty(ref xevent, buffer, len);
Marshal.FreeHGlobal(buffer);
}
public void SetHtmlData(object data, ref XEvent xevent)
{
IntPtr buffer;
int len;
string str = data as string;
if (str == null)
return;
if (xevent.SelectionRequestEvent.target == (IntPtr)Atom.XA_STRING)
{
byte[] bytes = Encoding.ASCII.GetBytes(str);
buffer = Marshal.AllocHGlobal(bytes.Length);
len = bytes.Length;
for (int i = 0; i < len; i++)
Marshal.WriteByte(buffer, i, bytes[i]);
}
else
{
buffer = Marshal.StringToHGlobalAnsi(str);
len = 0;
while (Marshal.ReadByte(buffer, len) != 0)
len++;
}
SetProperty(ref xevent, buffer, len);
Marshal.FreeHGlobal(buffer);
}
public void SetFileNameData(object data, ref XEvent xevent)
{
var uri_list = data as IEnumerable<string>;
//if (uri_list == null)
//{
// IDataObject dobj = data as IDataObject;
// if (dobj == null)
// return;
// uri_list = dobj.GetData(DataFormats.FileDrop, true) as string[];
//}
if (uri_list == null)
return;
StringBuilder res = new StringBuilder();
foreach (string uri_str in uri_list)
{
Uri uri = new Uri(uri_str);
res.Append(uri.ToString());
res.Append("\r\n");
}
IntPtr buffer = Marshal.StringToHGlobalAnsi((string)res.ToString());
int len = 0;
while (Marshal.ReadByte(buffer, len) != 0)
len++;
SetProperty(ref xevent, buffer, len);
}
private void SetProperty(ref XEvent xevent, IntPtr data, int length)
{
XEvent sel = new XEvent();
sel.SelectionEvent.type = XEventName.SelectionNotify;
sel.SelectionEvent.send_event = true;
sel.SelectionEvent.display = x11Info.Display;
sel.SelectionEvent.selection = xevent.SelectionRequestEvent.selection;
sel.SelectionEvent.target = xevent.SelectionRequestEvent.target;
sel.SelectionEvent.requestor = xevent.SelectionRequestEvent.requestor;
sel.SelectionEvent.time = xevent.SelectionRequestEvent.time;
sel.SelectionEvent.property = xevent.SelectionRequestEvent.property;
XLib.XChangeProperty(x11Info.Display, xevent.SelectionRequestEvent.requestor,
xevent.SelectionRequestEvent.property,
xevent.SelectionRequestEvent.target,
8, PropertyMode.Replace, data, length);
XLib.XSendEvent(x11Info.Display, xevent.SelectionRequestEvent.requestor, true,
(IntPtr)EventMask.NoEventMask, ref sel);
return;
}
public bool HandleMouseOver()
{
IntPtr toplevel, window;
int x_root, y_root;
GetWindowsUnderPointer(out window, out toplevel, out x_root, out y_root);
if (window != LastWindow && State == DragState.Entered)
{
State = DragState.Dragging;
// TODO: Send a Leave if this is an MWF window
if (toplevel != TargetWindow)
SendLeave(TargetWindow, toplevel);
}
State = DragState.Entered;
if (toplevel != TargetWindow)
{
if (cancelHandle == null || cancelHandle.Cancel)
{
return true;
}
// Entering a new toplevel window
SendEnter(toplevel, SourceWindow, SupportedTypes);
}
else
{
// Already in a toplevel window, so send a position
SendPosition(toplevel, SourceWindow,
Action,
x_root, y_root,
IntPtr.Zero);
}
TargetWindow = toplevel;
LastWindow = window;
return true;
}
void GetWindowsUnderPointer(out IntPtr window, out IntPtr toplevel, out int x_root, out int y_root)
{
toplevel = IntPtr.Zero;
window = x11Info.RootWindow;
IntPtr root, child;
bool dnd_aware = false;
int x_temp, y_temp;
int mask_return;
int x = x_root = (int)point.X;
int y = y_root = (int)point.Y;
while (XLib.XQueryPointer(x11Info.Display, window, out root, out child,
out x_temp, out y_temp, out x, out y, out mask_return))
{
if (!dnd_aware)
{
dnd_aware = IsWindowDndAware(window);
if (dnd_aware)
{
toplevel = window;
x_root = x_temp;
y_root = y_temp;
}
}
if (child == IntPtr.Zero)
break;
window = child;
}
}
private void SendDrop(IntPtr handle, IntPtr from, IntPtr time)
{
XEvent xevent = new XEvent();
xevent.AnyEvent.type = XEventName.ClientMessage;
xevent.AnyEvent.display = x11Info.Display;
xevent.ClientMessageEvent.window = handle;
xevent.ClientMessageEvent.message_type = x11Info.Atoms.XdndDrop;
xevent.ClientMessageEvent.format = 32;
xevent.ClientMessageEvent.ptr1 = from;
xevent.ClientMessageEvent.ptr3 = time;
XLib.XSendEvent(x11Info.Display, handle, false, IntPtr.Zero, ref xevent);
//dropped = true;
}
private void SendPosition(IntPtr handle, IntPtr from, IntPtr action, int x, int y, IntPtr time)
{
XEvent xevent = new XEvent();
xevent.AnyEvent.type = XEventName.ClientMessage;
xevent.AnyEvent.display = x11Info.Display;
xevent.ClientMessageEvent.window = handle;
xevent.ClientMessageEvent.message_type = x11Info.Atoms.XdndPosition;
xevent.ClientMessageEvent.format = 32;
xevent.ClientMessageEvent.ptr1 = from;
xevent.ClientMessageEvent.ptr3 = (IntPtr)((x << 16) | (y & 0xFFFF));
xevent.ClientMessageEvent.ptr4 = time;
xevent.ClientMessageEvent.ptr5 = action;
XLib.XSendEvent(x11Info.Display, handle, false, IntPtr.Zero, ref xevent);
}
private void SendLeave(IntPtr handle, IntPtr from)
{
XEvent xevent = new XEvent();
xevent.AnyEvent.type = XEventName.ClientMessage;
xevent.AnyEvent.display = x11Info.Display;
xevent.ClientMessageEvent.window = handle;
xevent.ClientMessageEvent.message_type = x11Info.Atoms.XdndLeave;
xevent.ClientMessageEvent.format = 32;
xevent.ClientMessageEvent.ptr1 = from;
XLib.XSendEvent(x11Info.Display, handle, false, IntPtr.Zero, ref xevent);
}
public void SendFinished()
{
XEvent xevent = new XEvent();
xevent.AnyEvent.type = XEventName.ClientMessage;
xevent.AnyEvent.display = x11Info.Display;
xevent.ClientMessageEvent.window = SourceWindow;
xevent.ClientMessageEvent.message_type = x11Info.Atoms.XdndFinished;
xevent.ClientMessageEvent.format = 32;
xevent.ClientMessageEvent.ptr1 = TargetWindow;
XLib.XSendEvent(x11Info.Display, SourceWindow, false, IntPtr.Zero, ref xevent);
XLib.XFlush(x11Info.Display);
}
private bool IsWindowDndAware(IntPtr handle)
{
bool res = true;
// Check the version number, we need greater than 3
IntPtr actual;
int format;
IntPtr count;
IntPtr remaining;
IntPtr data = IntPtr.Zero;
XLib.XGetWindowProperty(x11Info.Display, handle, x11Info.Atoms.XdndAware, IntPtr.Zero, new IntPtr(0x8000000), false,
(IntPtr)Atom.XA_ATOM, out actual, out format,
out count, out remaining, out data);
if (actual != (IntPtr)Atom.XA_ATOM || format != 32 ||
count.ToInt32() == 0 || data == IntPtr.Zero)
{
if (data != IntPtr.Zero)
XLib.XFree(data);
return false;
}
int version = Marshal.ReadInt32(data, 0);
if (version < 3)
{
Console.Error.WriteLine("XDND Version too old (" + version + ").");
XLib.XFree(data);
return false;
}
// First type is actually the XDND version
if (count.ToInt32() > 1)
{
res = false;
for (int i = 1; i < count.ToInt32(); i++)
{
IntPtr type = (IntPtr)Marshal.ReadInt32(data, i *
Marshal.SizeOf(typeof(int)));
for (int j = 0; j < SupportedTypes.Length; j++)
{
if (SupportedTypes[j] == type)
{
res = true;
break;
}
}
}
}
XLib.XFree(data);
return res;
}
private enum DragState
{
None,
Beginning,
Dragging,
Entered
}
}
}

282
CPF.Linux/Glib.cs Normal file
View File

@ -0,0 +1,282 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using CPF.Platform;
namespace CPF.Linux
{
static unsafe class Glib
{
private const string GlibName = "libglib-2.0.so.0";
private const string GObjectName = "libgobject-2.0.so.0";
[DllImport(GlibName)]
public static extern void g_slist_free(GSList* data);
[DllImport(GObjectName)]
private static extern void g_object_ref(IntPtr instance);
[DllImport(GObjectName)]
private static extern ulong g_signal_connect_object(IntPtr instance, Utf8Buffer signal,
IntPtr handler, IntPtr userData, int flags);
[DllImport(GObjectName)]
private static extern void g_object_unref(IntPtr instance);
[DllImport(GObjectName)]
private static extern ulong g_signal_handler_disconnect(IntPtr instance, ulong connectionId);
private delegate bool timeout_callback(IntPtr data);
[DllImport(GlibName)]
private static extern ulong g_timeout_add_full(int prio, uint interval, timeout_callback callback, IntPtr data,
IntPtr destroy);
class ConnectedSignal : IDisposable
{
private readonly IntPtr _instance;
private GCHandle _handle;
private readonly ulong _id;
public ConnectedSignal(IntPtr instance, GCHandle handle, ulong id)
{
_instance = instance;
g_object_ref(instance);
_handle = handle;
_id = id;
}
public void Dispose()
{
if (_handle.IsAllocated)
{
g_signal_handler_disconnect(_instance, _id);
g_object_unref(_instance);
_handle.Free();
}
}
}
public static IDisposable ConnectSignal<T>(IntPtr obj, string name, T handler)
{
var handle = GCHandle.Alloc(handler);
var ptr = Marshal.GetFunctionPointerForDelegate((Delegate)(object)handler);
using (var utf = new Utf8Buffer(name))
{
var id = g_signal_connect_object(obj, utf, ptr, IntPtr.Zero, 0);
if (id == 0)
throw new ArgumentException("Unable to connect to signal " + name);
return new ConnectedSignal(obj, handle, id);
}
}
static bool TimeoutHandler(IntPtr data)
{
var handle = GCHandle.FromIntPtr(data);
var cb = (Func<bool>)handle.Target;
if (!cb())
{
handle.Free();
return false;
}
return true;
}
private static readonly timeout_callback s_pinnedHandler;
static Glib()
{
s_pinnedHandler = TimeoutHandler;
}
static void AddTimeout(int priority, uint interval, Func<bool> callback)
{
var handle = GCHandle.Alloc(callback);
g_timeout_add_full(priority, interval, s_pinnedHandler, GCHandle.ToIntPtr(handle), IntPtr.Zero);
}
public static Task<T> RunOnGlibThread<T>(Func<T> action)
{
var tcs = new TaskCompletionSource<T>();
AddTimeout(0, 0, () =>
{
try
{
tcs.SetResult(action());
}
catch (Exception e)
{
tcs.TrySetException(e);
}
return false;
});
return tcs.Task;
}
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct GSList
{
public readonly IntPtr Data;
public readonly GSList* Next;
}
public enum GtkFileChooserAction
{
Open,
Save,
SelectFolder,
}
// ReSharper disable UnusedMember.Global
public enum GtkResponseType
{
Help = -11,
Apply = -10,
No = -9,
Yes = -8,
Close = -7,
Cancel = -6,
Ok = -5,
DeleteEvent = -4,
Accept = -3,
Reject = -2,
None = -1,
}
// ReSharper restore UnusedMember.Global
public static unsafe class Gtk
{
private static IntPtr s_display;
private const string GdkName = "libgdk-3.so.0";
private const string GtkName = "libgtk-3.so.0";
[DllImport(GtkName)]
static extern void gtk_main_iteration();
[DllImport(GtkName)]
public static extern void gtk_window_set_modal(IntPtr window, bool modal);
[DllImport(GtkName)]
public static extern void gtk_window_present(IntPtr gtkWindow);
public delegate bool signal_generic(IntPtr gtkWidget, IntPtr userData);
public delegate bool signal_dialog_response(IntPtr gtkWidget, GtkResponseType response, IntPtr userData);
[DllImport(GtkName)]
public static extern IntPtr gtk_file_chooser_dialog_new(Utf8Buffer title, IntPtr parent,
GtkFileChooserAction action, IntPtr ignore);
[DllImport(GtkName)]
public static extern void gtk_file_chooser_set_select_multiple(IntPtr chooser, bool allow);
[DllImport(GtkName)]
public static extern void
gtk_dialog_add_button(IntPtr raw, Utf8Buffer button_text, GtkResponseType response_id);
[DllImport(GtkName)]
public static extern GSList* gtk_file_chooser_get_filenames(IntPtr chooser);
[DllImport(GtkName)]
public static extern void gtk_file_chooser_set_filename(IntPtr chooser, Utf8Buffer file);
[DllImport(GtkName)]
public static extern void gtk_file_chooser_set_current_name(IntPtr chooser, Utf8Buffer file);
[DllImport(GtkName)]
public static extern IntPtr gtk_file_chooser_get_filter(IntPtr chooser);
[DllImport(GtkName)]
public static extern IntPtr gtk_file_filter_new();
[DllImport(GtkName)]
public static extern IntPtr gtk_file_filter_set_name(IntPtr filter, Utf8Buffer name);
[DllImport(GtkName)]
public static extern IntPtr gtk_file_filter_add_pattern(IntPtr filter, Utf8Buffer pattern);
[DllImport(GtkName)]
public static extern IntPtr gtk_file_chooser_add_filter(IntPtr chooser, IntPtr filter);
[DllImport(GtkName)]
public static extern void gtk_widget_realize(IntPtr gtkWidget);
[DllImport(GtkName)]
public static extern IntPtr gtk_widget_get_window(IntPtr gtkWidget);
[DllImport(GtkName)]
public static extern void gtk_widget_hide(IntPtr gtkWidget);
[DllImport(GtkName)]
static extern bool gtk_init_check(int argc, IntPtr argv);
[DllImport(GdkName)]
static extern IntPtr gdk_x11_window_foreign_new_for_display(IntPtr display, IntPtr xid);
[DllImport(GdkName)]
static extern IntPtr gdk_set_allowed_backends(Utf8Buffer backends);
[DllImport(GdkName)]
static extern IntPtr gdk_display_get_default();
[DllImport(GtkName)]
static extern IntPtr gtk_application_new(Utf8Buffer appId, int flags);
[DllImport(GdkName)]
public static extern void gdk_window_set_transient_for(IntPtr window, IntPtr parent);
public static IntPtr GetForeignWindow(IntPtr xid) => gdk_x11_window_foreign_new_for_display(s_display, xid);
public static Task<bool> StartGtk()
{
var tcs = new TaskCompletionSource<bool>();
new Thread(() =>
{
try
{
using (var backends = new Utf8Buffer("x11"))
gdk_set_allowed_backends(backends);
}
catch
{
//Ignore
}
Environment.SetEnvironmentVariable("WAYLAND_DISPLAY",
"/proc/fake-display-to-prevent-wayland-initialization-by-gtk3");
if (!gtk_init_check(0, IntPtr.Zero))
{
tcs.SetResult(false);
return;
}
IntPtr app;
using (var utf = new Utf8Buffer($"cpf.app.a{Guid.NewGuid():N}"))
app = gtk_application_new(utf, 0);
if (app == IntPtr.Zero)
{
tcs.SetResult(false);
return;
}
s_display = gdk_display_get_default();
tcs.SetResult(true);
while (true)
gtk_main_iteration();
})
{ Name = "GTK3THREAD", IsBackground = true }.Start();
return tcs.Task;
}
}
}

788
CPF.Linux/LinuxPlatform.cs Normal file
View File

@ -0,0 +1,788 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using CPF;
using CPF.Drawing;
using CPF.Input;
using CPF.Platform;
using System.Threading.Tasks;
using CPF.Controls;
using System.Linq;
using System.Diagnostics;
using static CPF.Linux.XLib;
using static CPF.Linux.Glib;
using static CPF.Linux.Gtk;
using System.Net.Sockets;
using System.Net;
using System.IO;
namespace CPF.Linux
{
public class LinuxPlatform : RuntimePlatform
{
public static LinuxPlatform Platform;
////[DllImport("libXlibDemo.so")]
////public static extern IntPtr OpenIM(IntPtr dpy);
////[DllImport("libXlibDemo.so")]
////public static extern IntPtr CreateIC(IntPtr xim, IntPtr win);
//[DllImport("libXlibDemo.so")]
//public static extern void Move(IntPtr win, IntPtr dpy, ref XEvent xEvent);
///// <summary>
///// https://blog.csdn.net/qq_32768743/article/details/90605212
///// </summary>
//public static void Test()
//{
// Console.WriteLine(setlocale(6, ""));
// var dpy = XOpenDisplay(IntPtr.Zero);
// XSupportsLocale();
// Console.WriteLine(XSetLocaleModifiers(""));
// IntPtr default_string = IntPtr.Zero;
// var fontset = XCreateFontSet(dpy, "-*-*-medium-r-normal-*-*-120-*-*-*-*", out var missing_charsets, out var num_missing_charsets, ref default_string);
// Console.WriteLine("default_string:" + Marshal.PtrToStringUni(default_string));
// //Thread.Sleep(10000);
// var screen = XDefaultScreen(dpy);
// var win = XCreateSimpleWindow(dpy, XRootWindow(dpy, screen), 0, 0, 500, 200,
// 2, XBlackPixel(dpy, screen), XWhitePixel(dpy, screen));
// var gc = XCreateGC(dpy, win, 0, IntPtr.Zero);
// XSetForeground(dpy, gc, XWhitePixel(dpy, screen));
// XSetBackground(dpy, gc, XBlackPixel(dpy, screen));
// var im = XOpenIM(dpy, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
// var list = XVaCreateNestedList(0, XNames.XNFontSet, fontset, IntPtr.Zero);
// var best_style = XIMProperties.XIMPreeditNothing | XIMProperties.XIMStatusNothing;
// Console.WriteLine(string.Join("-", X11Window.GetSupportedInputStyles(im)));
// var ic = XCreateIC(im,
// XNames.XNInputStyle, best_style,
// XNames.XNClientWindow, win,
// IntPtr.Zero);
// XFree(list);
// long im_event_mask = 0;
// XGetICValues(ic, XNames.XNFilterEvents, ref im_event_mask, IntPtr.Zero);
// XSelectInput(dpy, win, (IntPtr)((long)(XEventMask.ExposureMask | XEventMask.KeyPressMask
// | XEventMask.StructureNotifyMask) | im_event_mask));
// XSetICFocus(ic);
// XMapWindow(dpy, win);
// //XRectangle preedit_area = new XRectangle();
// //XRectangle status_area = new XRectangle();
// while (true)
// {
// XNextEvent(dpy, out var xEvent);
// if (XFilterEvent(ref xEvent, IntPtr.Zero))
// continue;
// //switch (xEvent.type)
// //{
// // case XEventName.KeyPress:
// // break;
// // case XEventName.ConfigureNotify:
// // if ((best_style & XIMProperties.XIMPreeditArea) != 0)
// // {
// // preedit_area.width = (ushort)(xEvent.ConfigureEvent.width * 4 / 5);
// // preedit_area.height = 0;
// // GetPreferredGeometry(ic, XNames.XNPreeditAttributes, ref preedit_area);
// // preedit_area.x = (short)(xEvent.ConfigureEvent.width - preedit_area.width);
// // preedit_area.y = (short)(xEvent.ConfigureEvent.height - preedit_area.height);
// // SetGeometry(ic, XNames.XNPreeditAttributes, preedit_area);
// // }
// // if ((best_style & XIMProperties.XIMStatusArea) != 0)
// // {
// // status_area.width = (ushort)(xEvent.ConfigureEvent.width / 5);
// // status_area.height = 0;
// // GetPreferredGeometry(ic, XNames.XNStatusAttributes, ref status_area);
// // status_area.x = 0;
// // status_area.y = (short)(xEvent.ConfigureEvent.height - status_area.height);
// // SetGeometry(ic, XNames.XNStatusAttributes, status_area);
// // }
// // break;
// // default:
// // break;
// //}
// }
//}
//static void GetPreferredGeometry(IntPtr ic, string name, ref XRectangle area)
////XIC ic;
////char *name; /* XNPreEditAttributes or XNStatusAttributes */
////XRectangle *area; /* the constraints on the area */
//{
// var list = XVaCreateNestedList(0, "areaNeeded", ref area, IntPtr.Zero);
// /* set the constraints */
// XSetICValues(ic, name, list, IntPtr.Zero);
// /* Now query the preferred size */
// /* The Xsi input method, Xwnmo, seems to ignore the constraints, */
// /* but were not going to try to enforce them here. */
// XGetICValues(ic, name, list, IntPtr.Zero);
// XFree(list);
//}
////XIC ic;
////char *name; /* XNPreEditAttributes or XNStatusAttributes */
////XRectangle *area; /* the actual area to set */
//static unsafe void SetGeometry(IntPtr ic, string name, XRectangle area)
//{
// var list = XVaCreateNestedList(0, "area", ref area, IntPtr.Zero);
// XSetICValues(ic, name, list, IntPtr.Zero);
// XFree(list);
//}
enum EventCodes
{
X11 = 1,
Signal = 2
}
//private int _sigread, _sigwrite;
//private int _epoll;
private object _lock = new object();
//private bool _signaled;
static Pollfd[] pollfds; // For watching the X11 socket
static Socket listen; //
static Socket wake; //
static Socket wake_receive; //
static byte[] network_buffer; //
/// <summary>
/// 启用触摸事件之所有加上这个因为部分Linux对XI2支持有问题
/// </summary>
public bool EnabledTouch { get; set; }
public unsafe LinuxPlatform()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
Console.WriteLine($"系统架构:{RuntimeInformation.OSArchitecture}");
Console.WriteLine($"系统名称:{RuntimeInformation.OSDescription}");
Console.WriteLine($"进程架构:{RuntimeInformation.ProcessArchitecture}");
Console.WriteLine($"是否64位操作系统{Environment.Is64BitOperatingSystem}");
Console.WriteLine("CPU CORE:" + Environment.ProcessorCount);
Console.WriteLine("HostName:" + Environment.MachineName);
Console.WriteLine("Version:" + Environment.OSVersion);
Console.WriteLine("CPF Version:" + typeof(LinuxPlatform).Assembly.GetName().Version);
Platform = this;
XInitThreads();
Display = XOpenDisplay(IntPtr.Zero);
//Console.WriteLine(setlocale(6, ""));
//DeferredDisplay = XOpenDisplay(IntPtr.Zero);
if (Display == IntPtr.Zero)
throw new Exception("XOpenDisplay failed");
XError.Init();
Info = new X11Info(Display, DeferredDisplay);
Thread.CurrentThread.Name = "主线程";
_cursors = Enum.GetValues(typeof(CursorFontShape)).Cast<CursorFontShape>()
.ToDictionary(id => id, id => XLib.XCreateFontCursor(Display, id));
if (Info.XInputVersion != null && EnabledTouch)
{
var xi2 = new XI2Manager();
if (xi2.Init(this))
XI2 = xi2;
}
var fd = XLib.XConnectionNumber(Display);
//var ev = new epoll_event()
//{
// events = EPOLLIN,
// data = { u32 = (int)EventCodes.X11 }
//};
//_epoll = epoll_create1(0);
//if (_epoll == -1)
// throw new Exception("epoll_create1 failed");
//if (epoll_ctl(_epoll, EPOLL_CTL_ADD, fd, ref ev) == -1)
// throw new Exception("Unable to attach X11 connection handle to epoll");
//var fds = stackalloc int[2];
////pipe2(fds, O_NONBLOCK);
//if (pipe2(fds, O_NONBLOCK) == -1)
// throw new Exception("Unable to create X11 pipe");
//_sigread = fds[0];
//_sigwrite = fds[1];
//ev = new epoll_event
//{
// events = EPOLLIN,
// data = { u32 = (int)EventCodes.Signal }
//};
//if (epoll_ctl(_epoll, EPOLL_CTL_ADD, _sigread, ref ev) == -1)
// throw new Exception("Unable to attach signal pipe to epoll");
// For sleeping on the X11 socket
listen = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
IPEndPoint ep = new IPEndPoint(IPAddress.Loopback, 0);
listen.Bind(ep);
listen.Listen(1);
// To wake up when a timer is ready
network_buffer = new byte[10];
wake = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
wake.Connect(listen.LocalEndPoint);
// Make this non-blocking, so it doesn't
// deadlock if too many wakes are sent
// before the wake_receive end is polled
wake.Blocking = false;
wake_receive = listen.Accept();
pollfds = new Pollfd[2];
pollfds[0] = new Pollfd();
pollfds[0].fd = fd;
pollfds[0].events = PollEvents.POLLIN;
pollfds[1] = new Pollfd();
pollfds[1].fd = wake_receive.Handle.ToInt32();
pollfds[1].events = PollEvents.POLLIN;
XrmInitialize(); /* Need to initialize the DB before calling Xrm* functions */
}
}
public XI2Manager XI2;
public X11Info Info { get; private set; }
public IntPtr DeferredDisplay { get; set; }
public IntPtr Display { get; set; }
public override PixelPoint MousePosition
{
get
{
var po = GetCursorPos(Info);
//Console.WriteLine(po);
return new PixelPoint(po.x, po.y);
}
}
public override TimeSpan DoubleClickTime
{
get
{
return TimeSpan.FromSeconds(0.4);
}
}
public override IPopupImpl CreatePopup()
{
return new PopWindow();
}
public override IWindowImpl CreateWindow()
{
return new X11Window();
}
DataObject dataObject;
public override DragDropEffects DoDragDrop(DragDropEffects allowedEffects, params (DataFormat, object)[] data)
{
dataObject = new DataObject();
dataObject.StartDrag(allowedEffects, data);
return allowedEffects;
}
public override IReadOnlyList<Screen> GetAllScreen()
{
return ScreenImpl.Screens;
}
public override IClipboard GetClipboard()
{
return new X11Clipboard();
}
private Dictionary<CursorFontShape, IntPtr> _cursors;
private static readonly Dictionary<Cursors, CursorFontShape> s_mapping =
new Dictionary<Cursors, CursorFontShape>
{
{Cursors.Arrow, CursorFontShape.XC_top_left_arrow},
{Cursors.Cross, CursorFontShape.XC_cross},
{Cursors.Hand, CursorFontShape.XC_hand1},
{Cursors.Help, CursorFontShape.XC_question_arrow},
{Cursors.Ibeam, CursorFontShape.XC_xterm},
{Cursors.No, CursorFontShape.XC_X_cursor},
{Cursors.Wait, CursorFontShape.XC_watch},
{Cursors.AppStarting, CursorFontShape.XC_watch},
{Cursors.BottomSide, CursorFontShape.XC_bottom_side},
{Cursors.DragCopy, CursorFontShape.XC_center_ptr},
{Cursors.DragLink, CursorFontShape.XC_fleur},
{Cursors.DragMove, CursorFontShape.XC_diamond_cross},
{Cursors.LeftSide, CursorFontShape.XC_left_side},
{Cursors.RightSide, CursorFontShape.XC_right_side},
{Cursors.SizeAll, CursorFontShape.XC_sizing},
{Cursors.TopSide, CursorFontShape.XC_top_side},
{Cursors.UpArrow, CursorFontShape.XC_sb_up_arrow},
{Cursors.BottomLeftCorner, CursorFontShape.XC_bottom_left_corner},
{Cursors.BottomRightCorner, CursorFontShape.XC_bottom_right_corner},
{Cursors.SizeNorthSouth, CursorFontShape.XC_sb_v_double_arrow},
{Cursors.SizeWestEast, CursorFontShape.XC_sb_h_double_arrow},
{Cursors.TopLeftCorner, CursorFontShape.XC_top_left_corner},
{Cursors.TopRightCorner, CursorFontShape.XC_top_right_corner},
};
public override object GetCursor(Cursors cursorType)
{
IntPtr handle;
//if (cursorType == Cursors.None)
//{
// handle = _nullCursor;
//}
//else
//{
handle = s_mapping.TryGetValue(cursorType, out var shape)
? _cursors[shape]
: _cursors[CursorFontShape.XC_top_left_arrow];
//}
return handle;
}
public override SynchronizationContext GetSynchronizationContext()
{
return new LinuxSynchronizationContext();
}
static Dictionary<KeyGesture, PlatformHotkey> keyValuePairs = new Dictionary<KeyGesture, PlatformHotkey>() {
{ new KeyGesture(Keys.C,InputModifiers.Control),PlatformHotkey.Copy},
{ new KeyGesture(Keys.X,InputModifiers.Control),PlatformHotkey.Cut},
{ new KeyGesture(Keys.V,InputModifiers.Control),PlatformHotkey.Paste},
{ new KeyGesture(Keys.Y,InputModifiers.Control),PlatformHotkey.Redo},
{ new KeyGesture(Keys.A,InputModifiers.Control),PlatformHotkey.SelectAll},
{ new KeyGesture(Keys.Z,InputModifiers.Control),PlatformHotkey.Undo},
};
public override PlatformHotkey Hotkey(KeyGesture keyGesture)
{
keyValuePairs.TryGetValue(keyGesture, out PlatformHotkey platformHotkey);
return platformHotkey;
}
internal Dictionary<IntPtr, XWindow> windows = new Dictionary<IntPtr, XWindow>();
internal bool run = true;
public unsafe override void Run()
{
//X11Window x11Window = new X11Window();
//x11Window.SetVisible(true);
DoTask();
while (run)
{
XSync(Display, false);
while (XPending(Display) != 0)
{
XNextEvent(Display, out var xev);
if (XFilterEvent(ref xev, IntPtr.Zero))
{
continue;
}
OnEvent(ref xev, null);
}
//Invoke();
//Thread.Sleep(1);
//lock (_lock)
//{
// _signaled = false;
// int buf = 0;
// while (read(_sigread, &buf, new IntPtr(4)).ToInt64() > 0)
// {
// }
//}
DoTask();
Wait();
}
XCloseDisplay(Display);
}
public override void Run(CancellationToken cancellation)
{
//Console.WriteLine("开始循环");
while (!cancellation.IsCancellationRequested && run)
{
XSync(Display, false);
while (XPending(Display) != 0)
{
XNextEvent(Display, out var xev);
if (XFilterEvent(ref xev, IntPtr.Zero))
{
continue;
}
OnEvent(ref xev, null);
}
DoTask();
if (cancellation.IsCancellationRequested)
{
break;
}
Wait();
}
//Console.WriteLine("结束循环" + cancellation.IsCancellationRequested + run);
}
private unsafe void Wait()
{
XFlush(Display);
if (XPending(Display) == 0)
{
//if (run)
//{
//epoll_event ev;
// epoll_wait(_epoll, &ev, 1, -1);//-1永久阻塞直到有信号,
//}
poll(pollfds, (uint)pollfds.Length, -1);
// Clean out buffer, so we're not busy-looping on the same data
//if (length == pollfds.Length)
{
if (pollfds[1].revents != 0)
wake_receive.Receive(network_buffer, 0, 1, SocketFlags.None);
}
}
CPF.Threading.DispatcherTimer.SetTimeTick();
}
HashSet<XEventHandler> handlers = new HashSet<XEventHandler>();
public unsafe void RunMainLoop(CancelHandle cancelHandle, XEventHandler action)
{
handlers.Add(action);
while (!cancelHandle.Cancel && run)
{
XSync(Display, false);
while (XPending(Display) != 0)
{
XNextEvent(Display, out var xev);
if (XFilterEvent(ref xev, IntPtr.Zero))
{
continue;
}
if (!action(ref xev))
{
OnEvent(ref xev, action);
}
}
//Invoke();
//Thread.Sleep(1);
//lock (_lock)
//{
// _signaled = false;
// int buf = 0;
// while (read(_sigread, &buf, new IntPtr(4)).ToInt64() > 0)
// {
// }
//}
DoTask();
Wait();
}
handlers.Remove(action);
}
internal unsafe void SetFlag()
{
//lock (_lock)
//{
// if (_signaled)
// return;
// _signaled = true;
// int buf = 0;
// write(_sigwrite, &buf, new IntPtr(1));
//}
try
{
wake.Send(new byte[] { 0xFF });
}
catch (SocketException ex)
{
if (ex.SocketErrorCode != SocketError.WouldBlock)
{
throw;
}
}
}
void Invoke()
{
if (LinuxSynchronizationContext.asyncQueue.TryDequeue(out SendOrPostData postData))
{
postData.SendOrPostCallback(postData.Data);
}
if (LinuxSynchronizationContext.invokeQueue.TryDequeue(out SendOrPostData result))
{
result.SendOrPostCallback(result.Data);
result.ManualResetEvent.Set();
}
}
unsafe void OnEvent(ref XEvent xev, XEventHandler action)
{
foreach (var item in handlers.Reverse())
{
if (action != item)
{
if (item(ref xev))
{
return;
}
}
}
//if (xev.type == XEventName.GenericEvent)
// fixed (void* data = &xev.GenericEventCookie)
// {
// XGetEventData(Display, data);
// }
if (windows.TryGetValue(xev.AnyEvent.window, out var window))
{
lock (XWindow.XlibLock)
{
window.OnEvent(ref xev);
}
}
else if (xev.type == XEventName.GenericEvent && LinuxPlatform.Platform.XI2 != null)
{
fixed (void* data = &xev.GenericEventCookie)
{
XGetEventData(Info.Display, data);
try
{
if (Info.XInputOpcode ==
xev.GenericEventCookie.extension)
{
//Console.WriteLine((IntPtr)ev.GenericEventCookie.data);
var ev = (XIEvent*)xev.GenericEventCookie.data;
if (windows.TryGetValue(((XIDeviceEvent*)ev)->EventWindow, out window))
{
Platform.XI2.OnEvent(ev, (X11Window)window);
}
}
}
finally
{
if (xev.GenericEventCookie.data != null)
XFreeEventData(Info.Display, data);
}
}
}
//}
//finally
//{
// if (xev.type == XEventName.GenericEvent && xev.GenericEventCookie.data != null)
// XFreeEventData(Display, &xev.GenericEventCookie);
//}
}
private static unsafe void DoTask()
{
if (LinuxSynchronizationContext.invokeQueue.Count > 0)
{
while (LinuxSynchronizationContext.invokeQueue.TryDequeue(out var result))
{
result.SendOrPostCallback(result.Data);
result.ManualResetEvent.Set();
}
}
if (LinuxSynchronizationContext.asyncQueue.Count > 0)
{
while (LinuxSynchronizationContext.asyncQueue.TryDequeue(out SendOrPostData data))
{
data.SendOrPostCallback(data.Data);
}
}
}
void UpdateParent(IntPtr chooser, IWindowImpl parentWindow)
{
var xid = ((X11Window)parentWindow).Handle;
gtk_widget_realize(chooser);
var window = gtk_widget_get_window(chooser);
var parent = GetForeignWindow(xid);
if (window != IntPtr.Zero && parent != IntPtr.Zero)
gdk_window_set_transient_for(window, parent);
}
async Task EnsureInitialized()
{
if (_initialized == null) _initialized = StartGtk();
if (!(await _initialized))
throw new Exception("Unable to initialize GTK on separate thread");
}
private Task<bool> _initialized;
private unsafe Task<string[]> ShowDialog(string title, IWindowImpl parent, GtkFileChooserAction action,
bool multiSelect, string initialFileName, IEnumerable<FileDialogFilter> filters)
{
IntPtr dlg;
using (var name = new Utf8Buffer(title))
dlg = gtk_file_chooser_dialog_new(name, IntPtr.Zero, action, IntPtr.Zero);
UpdateParent(dlg, parent);
if (multiSelect)
gtk_file_chooser_set_select_multiple(dlg, true);
gtk_window_set_modal(dlg, true);
var tcs = new TaskCompletionSource<string[]>();
List<IDisposable> disposables = null;
if (filters != null)
foreach (var f in filters.Where(a => !string.IsNullOrWhiteSpace(a.Extensions)))
{
var filter = gtk_file_filter_new();
using (var b = new Utf8Buffer(f.Name))
gtk_file_filter_set_name(filter, b);
foreach (var e in f.Extensions.Split(',').Where(a => !string.IsNullOrWhiteSpace(a)).Select(a => a.Trim()))
using (var b = new Utf8Buffer("*." + e))
gtk_file_filter_add_pattern(filter, b);
gtk_file_chooser_add_filter(dlg, filter);
}
disposables = new List<IDisposable>
{
ConnectSignal<signal_generic>(dlg, "close", delegate
{
tcs.TrySetResult(null);
foreach (var d in disposables) d.Dispose();
disposables.Clear();
return false;
}),
ConnectSignal<signal_dialog_response>(dlg, "response", (_, resp, __) =>
{
string[] result = null;
if (resp == GtkResponseType.Accept)
{
var resultList = new List<string>();
var gs = gtk_file_chooser_get_filenames(dlg);
var cgs = gs;
while (cgs != null)
{
if (cgs->Data != IntPtr.Zero)
resultList.Add(Utf8Buffer.StringFromPtr(cgs->Data));
cgs = cgs->Next;
}
g_slist_free(gs);
result = resultList.ToArray();
}
gtk_widget_hide(dlg);
foreach (var d in disposables) d.Dispose();
disposables.Clear();
tcs.TrySetResult(result);
return false;
})
};
using (var open = new Utf8Buffer(
action == GtkFileChooserAction.Save ? "Save"
: action == GtkFileChooserAction.SelectFolder ? "Select"
: "Open"))
gtk_dialog_add_button(dlg, open, GtkResponseType.Accept);
using (var open = new Utf8Buffer("Cancel"))
gtk_dialog_add_button(dlg, open, GtkResponseType.Cancel);
if (initialFileName != null)
using (var fn = new Utf8Buffer(initialFileName))
{
if (action == GtkFileChooserAction.Save)
gtk_file_chooser_set_current_name(dlg, fn);
else
gtk_file_chooser_set_filename(dlg, fn);
}
gtk_window_present(dlg);
return tcs.Task;
}
string NameWithExtension(string path, string defaultExtension, FileDialogFilter filter)
{
var name = Path.GetFileName(path);
if (name != null && !name.Contains("."))
{
if (!string.IsNullOrWhiteSpace(filter?.Extensions))
{
if (defaultExtension != null
&& filter.Extensions.Contains(defaultExtension))
return path + "." + defaultExtension.TrimStart('.');
var ext = filter.Extensions.Split(',').FirstOrDefault(x => x != "*");
if (ext != null)
return path + "." + ext.TrimStart('.');
}
if (defaultExtension != null)
path += "." + defaultExtension.TrimStart('.');
}
return path;
}
public override async Task<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent)
{
await EnsureInitialized();
return await await RunOnGlibThread(
() => ShowDialog(dialog.Title, parent,
dialog is OpenFileDialog ? GtkFileChooserAction.Open : GtkFileChooserAction.Save,
(dialog as OpenFileDialog)?.AllowMultiple ?? false,
System.IO.Path.Combine(string.IsNullOrEmpty(dialog.Directory) ? "" : dialog.Directory,
string.IsNullOrEmpty(dialog.InitialFileName) ? "" : dialog.InitialFileName), dialog.Filters));
}
public override async Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent)
{
await EnsureInitialized();
return await await RunOnGlibThread(async () =>
{
var res = await ShowDialog(dialog.Title, parent,
GtkFileChooserAction.SelectFolder, false, dialog.Directory, null);
return res?.FirstOrDefault();
});
}
public override INativeImpl CreateNative()
{
return new NativeHost();
}
public override INotifyIconImpl CreateNotifyIcon()
{
return new NotifyIcon();
}
}
/// <summary>
/// 处理事件返回值为true的时候不调用默认处理方法
/// </summary>
/// <param name="xEvent"></param>
/// <returns></returns>
public delegate bool XEventHandler(ref XEvent xEvent);
public class CancelHandle
{
public bool Cancel { get; set; }
public object Data { get; set; }
/// <summary>
/// 设置数据同时设置Cancel=true
/// </summary>
/// <param name="data"></param>
public void SetResult(object data)
{
Data = data;
Cancel = true;
}
}
}

View File

@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Collections.Concurrent;
using CPF.Controls;
using CPF.Platform;
namespace CPF.Linux
{
class LinuxSynchronizationContext : SynchronizationContext
{
public static ConcurrentQueue<SendOrPostData> invokeQueue = new ConcurrentQueue<SendOrPostData>();
public static ConcurrentQueue<SendOrPostData> asyncQueue = new ConcurrentQueue<SendOrPostData>();
/// <summary>
/// 异步
/// </summary>
/// <param name="d"></param>
/// <param name="state"></param>
public override void Post(SendOrPostCallback d, object state)
{
//Console.WriteLine("发送BeginInvoke");
//lock (X11Window.XlibLock)
{
asyncQueue.Enqueue(new SendOrPostData { Data = state, SendOrPostCallback = d });
//SendMessage(X11Atoms.BeginInvoke, (IntPtr)1);
}
LinuxPlatform.Platform.SetFlag();
}
public override void Send(SendOrPostCallback d, object state)
{
//Console.WriteLine("发送Invoke");
if (Threading.Dispatcher.MainThread.CheckAccess())
{
d(state);
}
else
{
var invokeMre = new ManualResetEvent(false);
//lock (X11Window.XlibLock)
{
invokeQueue.Enqueue(new SendOrPostData { Data = state, SendOrPostCallback = d, ManualResetEvent = invokeMre });
//SendMessage(X11Atoms.Invoke, (IntPtr)2);
}
LinuxPlatform.Platform.SetFlag();
invokeMre.WaitOne();
}
}
void SendMessage(IntPtr message_type, IntPtr l1)
{
var xev = new XEvent
{
ClientMessageEvent =
{
type = XEventName.ClientMessage,
send_event = true,
window = X11Window.main.Handle,
message_type = message_type,
format = 32,
ptr1 = l1,
}
};
XLib.XSendEvent(LinuxPlatform.Platform.Display, X11Window.main.Handle, true,
new IntPtr((int)(EventMask.StructureNotifyMask)), ref xev);
XLib.XFlush(LinuxPlatform.Platform.Display);
}
}
class SendOrPostData
{
public SendOrPostCallback SendOrPostCallback;
public object Data;
public ManualResetEvent ManualResetEvent;
}
}

191
CPF.Linux/NativeHost.cs Normal file
View File

@ -0,0 +1,191 @@
using CPF.Controls;
using CPF.Drawing;
using CPF.Platform;
using System;
using System.Collections.Generic;
using System.Text;
namespace CPF.Linux
{
public class NativeHost : XWindow, INativeImpl
{
LinuxPlatform x11info;
XSetWindowAttributes attr = new XSetWindowAttributes();
protected override void OnCreateWindw()
{
x11info = LinuxPlatform.Platform;
var valueMask = default(SetWindowValuemask);
attr.background_pixel = IntPtr.Zero;
attr.border_pixel = IntPtr.Zero;
attr.backing_store = 1;
attr.bit_gravity = Gravity.NorthWestGravity;
attr.win_gravity = Gravity.NorthWestGravity;
valueMask |= SetWindowValuemask.BackPixel | SetWindowValuemask.BorderPixel | SetWindowValuemask.BackingStore | SetWindowValuemask.OverrideRedirect
//| SetWindowValuemask.BackPixmap
| SetWindowValuemask.BitGravity | SetWindowValuemask.WinGravity;
attr.colormap = XLib.XCreateColormap(x11info.Info.Display, x11info.Info.RootWindow, x11info.Info.TransparentVisualInfo.visual, 0);
valueMask |= SetWindowValuemask.ColorMap;
Handle = XLib.XCreateWindow(x11info.Display, x11info.Info.DefaultRootWindow, 0, 0, 100, 100, 0, 32, (int)CreateWindowArgs.InputOutput, x11info.Info.TransparentVisualInfo.visual, new UIntPtr((uint)valueMask), ref attr);
XEventMask ignoredMask = XEventMask.SubstructureRedirectMask | XEventMask.ResizeRedirectMask | XEventMask.PointerMotionHintMask;
var mask = new IntPtr(0xffffff ^ (int)ignoredMask);
XLib.XSelectInput(x11info.Display, Handle, mask);
EventAction = OnXEvent;
}
PixelSize size;
void OnXEvent(ref XEvent ev)
{
if (ev.type == XEventName.Expose)
{
var gc = XLib.XCreateGC(x11info.Display, Handle, 0, IntPtr.Zero);
XColor xcolor = new XColor();
xcolor.red = (ushort)(backcolor.R * 257);
xcolor.green = (ushort)(backcolor.G * 257);
xcolor.blue = (ushort)(backcolor.B * 257);
XLib.XAllocColor(x11info.Display, attr.colormap, ref xcolor);
XLib.XSetBackground(x11info.Display, gc, xcolor.pixel);
XLib.XSetForeground(x11info.Display, gc, xcolor.pixel);
XLib.XFillRectangle(x11info.Display, Handle, gc, 0, 0, size.Width, size.Height);
XLib.XFreeGC(x11info.Display, gc);
}
else if (ev.type == XEventName.ConfigureNotify)
{
size = new PixelSize(ev.ConfigureEvent.width, ev.ConfigureEvent.height);
}
}
Color backcolor;
public void SetBackColor(Color color)
{
backcolor = color;
Invalidate();
//var gc = XLib.XCreateGC(x11info.Display, Handle, 0, IntPtr.Zero);
//XColor xcolor = new XColor();
//xcolor.red = (ushort)(color.R * 257);
//xcolor.green = (ushort)(color.G * 257);
//xcolor.blue = (ushort)(color.B * 257);
//XLib.XAllocColor(x11info.Display, x11info.Info.DefaultColormap, ref xcolor);
//XLib.XSetBackground(x11info.Display, gc, xcolor.pixel);
//XLib.XFreeGC(x11info.Display, gc);
}
void Invalidate()
{
lock (XlibLock)
{
var xev = new XEvent
{
ExposeEvent =
{
type = XEventName.Expose,
send_event = true,
window = Handle,
count=1,
display=x11info.Display,
height=size.Height,
width= size.Width,
x=0,
y= 0
}
};
XLib.XSendEvent(x11info.Display, Handle, false,
new IntPtr((int)(EventMask.ExposureMask)), ref xev);
}
}
Rect? clipRect;
Rect? renderRect;
bool visible = false;
public void SetBounds(Rect renderRect, Rect rect, bool visible)
{
if (visible)
{
if (!float.IsInfinity(rect.Width) && !float.IsInfinity(rect.Height) && clipRect != rect)
{
clipRect = rect;
var rects = new XRectangle[] { new XRectangle { x = (short)(rect.X * parent.RenderScaling), y = (short)(rect.Y * parent.RenderScaling), width = (ushort)(rect.Width * parent.RenderScaling), height = (ushort)(rect.Height * parent.RenderScaling) } };
//Console.WriteLine(rect);
XLib.XShapeCombineRectangles(x11info.Display, Handle, XShapeKind.ShapeBounding, 0, 0, rects, rects.Length, XShapeOperation.ShapeSet, XOrdering.Unsorted);
}
if (this.renderRect != renderRect)
{
this.renderRect = renderRect;
XLib.XMoveResizeWindow(x11info.Display, Handle, (int)(renderRect.X * parent.RenderScaling), (int)(renderRect.Y * parent.RenderScaling), (int)(renderRect.Width * parent.RenderScaling), (int)(renderRect.Height * parent.RenderScaling));
}
var gc = XLib.XCreateGC(x11info.Display, Handle, 0, IntPtr.Zero);
XColor xcolor = new XColor();
xcolor.red = (ushort)(backcolor.R * 257);
xcolor.green = (ushort)(backcolor.G * 257);
xcolor.blue = (ushort)(backcolor.B * 257);
XLib.XAllocColor(x11info.Display, attr.colormap, ref xcolor);
XLib.XSetBackground(x11info.Display, gc, xcolor.pixel);
XLib.XSetForeground(x11info.Display, gc, xcolor.pixel);
XLib.XFillRectangle(x11info.Display, Handle, gc, 0, 0, size.Width, size.Height);
XLib.XFreeGC(x11info.Display, gc);
//Invalidate();
if (this.visible != visible)
{
XLib.XMapWindow(x11info.Display, Handle);
}
}
else
{
XLib.XUnmapWindow(x11info.Display, Handle);
}
this.visible = visible;
}
IntPtr contentHandle;
public void SetContent(object content)
{
if (content != null)
{
if (content is IntPtr intPtr)
{
contentHandle = intPtr;
if (contentHandle != IntPtr.Zero)
{
XLib.XReparentWindow(x11info.Display, intPtr, Handle, 0, 0);
}
}
else
{
throw new Exception("必须是控件句柄IntPtr类型");
}
}
else
{
if (contentHandle != IntPtr.Zero)
{
XLib.XReparentWindow(x11info.Display, contentHandle, x11info.Info.DefaultRootWindow, 0, 0);
}
contentHandle = IntPtr.Zero;
}
}
UIElement owner;
public void SetOwner(NativeElement owner)
{
this.owner = owner;
}
X11Window parent;
object INativeImpl.Handle => Handle;
public void SetParent(IViewImpl parent)
{
if (parent is X11Window window)
{
XLib.XReparentWindow(x11info.Display, Handle, window.Handle, 0, 0);
}
else
{
XLib.XReparentWindow(x11info.Display, Handle, x11info.Info.DefaultRootWindow, 0, 0);
}
this.parent = parent as X11Window;
}
}
}

377
CPF.Linux/NotifyIcon.cs Normal file
View File

@ -0,0 +1,377 @@
using CPF.Controls;
using CPF.Drawing;
using CPF.Input;
using CPF.Platform;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using static CPF.Linux.XLib;
namespace CPF.Linux
{
class NotifyIcon : XWindow, INotifyIconImpl
{
public NotifyIcon()
{
DisplayHandle = LinuxPlatform.Platform.Display;
}
LinuxPlatform x11info;
XSetWindowAttributes attr = new XSetWindowAttributes();
protected override void OnCreateWindw()
{
x11info = LinuxPlatform.Platform;
var valueMask = default(SetWindowValuemask);
attr.background_pixel = IntPtr.Zero;
attr.border_pixel = IntPtr.Zero;
attr.backing_store = 1;
attr.bit_gravity = Gravity.NorthWestGravity;
attr.win_gravity = Gravity.NorthWestGravity;
attr.override_redirect = true;
valueMask |= SetWindowValuemask.BackPixel | SetWindowValuemask.BorderPixel | SetWindowValuemask.BackingStore | SetWindowValuemask.OverrideRedirect
//| SetWindowValuemask.BackPixmap
| SetWindowValuemask.BitGravity | SetWindowValuemask.WinGravity;
valueMask |= SetWindowValuemask.SaveUnder;
attr.colormap = XLib.XCreateColormap(x11info.Info.Display, x11info.Info.RootWindow, x11info.Info.TransparentVisualInfo.visual, 0);
valueMask |= SetWindowValuemask.ColorMap;
Handle = XLib.XCreateWindow(x11info.Display, x11info.Info.DefaultRootWindow, 0, 0, 16, 16, 0, 32, (int)CreateWindowArgs.InputOutput, x11info.Info.TransparentVisualInfo.visual, new UIntPtr((uint)valueMask), ref attr);
//Handle= XCreateSimpleWindow(x11info.Display, x11info.Info.DefaultRootWindow, 0, 0, 16, 16, 0, IntPtr.Zero, XWhitePixel(x11info.Display,x11info.Info.DefaultScreen));
XEventMask ignoredMask = XEventMask.SubstructureRedirectMask | XEventMask.ResizeRedirectMask | XEventMask.PointerMotionHintMask;
var mask = new IntPtr(0xffffff ^ (int)ignoredMask);
XLib.XSelectInput(x11info.Display, Handle, mask);
EventAction = OnXEvent;
popup.Children.Add(textBlock);
}
Bitmap bitmap;
void OnXEvent(ref XEvent ev)
{
if (ev.type == XEventName.Expose)
{
XWindowAttributes Attr = new XWindowAttributes();
XGetWindowAttributes(DisplayHandle, Handle, ref Attr);
if (icon != null)
{
if (bitmap == null || bitmap.Width != Attr.width || bitmap.Height != Attr.height)
{
if (bitmap != null)
{
bitmap.Dispose();
}
bitmap = new Bitmap(Attr.width, Attr.height);
}
using (var dc = DrawingContext.FromBitmap(bitmap))
{
dc.AntialiasMode = AntialiasMode.AntiAlias;
dc.Clear(Color.Transparent);
dc.DrawImage(icon, new Rect(0, 0, bitmap.Width, bitmap.Height), new Rect(0, 0, icon.Width, icon.Height));
}
}
if (bitmap != null)
{
using (var l = bitmap.Lock())
{
var gc = XCreateGC(x11info.Display, Handle, 0, IntPtr.Zero);
//XLockDisplay(x11info.Display);
var img = new XImage();
int bitsPerPixel = 32;
img.width = bitmap.Width;
img.height = bitmap.Height;
img.format = 2; //ZPixmap;
img.data = l.DataPointer;
img.byte_order = 0;// LSBFirst;
img.bitmap_unit = bitsPerPixel;
img.bitmap_bit_order = 0;// LSBFirst;
img.bitmap_pad = bitsPerPixel;
img.depth = 32;
img.bytes_per_line = bitmap.Width * 4;
img.bits_per_pixel = bitsPerPixel;
XInitImage(ref img);
XPutImage(x11info.Display, Handle, gc, ref img, 0, 0, 0, 0, (uint)Attr.width, (uint)Attr.height);
//XSync(x11info.Display, false);
//XUnlockDisplay(x11info.Display);
XFreeGC(x11info.Display, gc);
//XFlush(x11info.Display);
//Console.WriteLine(Attr.width + "," + Attr.height);
}
}
}
else if (ev.type == XEventName.ButtonPress)
{
if (ev.ButtonEvent.button < 4 || ev.ButtonEvent.button == 8 || ev.ButtonEvent.button == 9)
{
MouseButton mouseButton = MouseButton.None;
switch (ev.ButtonEvent.button)
{
case 1:
mouseButton = MouseButton.Left;
break;
case 2:
mouseButton = MouseButton.Middle;
break;
case 3:
mouseButton = MouseButton.Right;
break;
case 8:
mouseButton = MouseButton.XButton1;
break;
case 9:
mouseButton = MouseButton.XButton2;
break;
}
if (MouseDown != null)
{
MouseDown(this, new NotifyIconMouseEventArgs(mouseButton));
}
if (mouseButton == MouseButton.Left)
{
var time = DateTime.Now;
if (mouseDownTime.HasValue)
{
if (time - mouseDownTime <= LinuxPlatform.Platform.DoubleClickTime)
{
if (DoubleClick != null)
{
DoubleClick(this, EventArgs.Empty);
}
mouseDownTime = null;
}
else
{
mouseDownTime = time;
}
}
else
{
mouseDownTime = time;
}
}
}
}
else if (ev.type == XEventName.ButtonRelease)
{
if (ev.ButtonEvent.button < 4 || ev.ButtonEvent.button == 8 || ev.ButtonEvent.button == 9)
{
MouseButton mouseButton = MouseButton.None;
switch (ev.ButtonEvent.button)
{
case 1:
mouseButton = MouseButton.Left;
break;
case 2:
mouseButton = MouseButton.Middle;
break;
case 3:
mouseButton = MouseButton.Right;
break;
case 8:
mouseButton = MouseButton.XButton1;
break;
case 9:
mouseButton = MouseButton.XButton2;
break;
}
if (MouseUp != null)
{
MouseUp(this, new NotifyIconMouseEventArgs(mouseButton));
}
if (mouseButton == MouseButton.Left)
{
if (Click != null)
{
Click(this, EventArgs.Empty);
}
}
}
}
else if (ev.type == XEventName.MotionNotify)
{
if (!isMouseEnter)
{
isMouseEnter = true;
if (!string.IsNullOrWhiteSpace(Text))
{
textBlock.Text = Text;
popup.Placement = PlacementMode.Absolute;
var p = MouseDevice.Location;
popup.MarginLeft = p.X / popup.LayoutScaling + 10;
if (p.X < 100)
{
popup.MarginTop = p.Y / popup.LayoutScaling + 10;
}
else
{
popup.MarginTop = p.Y / popup.LayoutScaling - 30;
}
popup.LoadStyle(Window.Windows.FirstOrDefault(a => a.IsMain));
popup.Show();
}
}
}
else if (ev.type == XEventName.LeaveNotify)
{
isMouseEnter = false;
if (popup.Visibility == Visibility.Visible)
{
popup.Width = "auto";
popup.Height = "auto";
popup.Hide();
}
}
}
TextBlock textBlock = new TextBlock { Margin = "2" };
Popup popup = new Popup
{
CanActivate = false,
StaysOpen = false,
BorderFill = "#aaa",
Background = "#fff",
BorderStroke = "1",
};
bool isMouseEnter;
IntPtr DisplayHandle;
IntPtr SystrayMgrWindow;
public string Text { get; set; }
Image icon;
public Image Icon
{
get { return icon; }
set
{
icon = value;
Invalidate();
}
}
private void Invalidate()
{
XWindowAttributes Attr = new XWindowAttributes();
XGetWindowAttributes(DisplayHandle, Handle, ref Attr);
var xev = new XEvent
{
ExposeEvent =
{
type = XEventName.Expose,
send_event = true,
window = Handle,
count=1,
display=x11info.Display,
height=Attr.height,
width= Attr.width,
}
};
lock (XlibLock)
{
XSendEvent(x11info.Display, Handle, false,
new IntPtr((int)(EventMask.ExposureMask)), ref xev);
}
}
//public ContextMenu ContextMenu { get; set; }
bool visible;
DateTime? mouseDownTime;
public event EventHandler Click;
public event EventHandler DoubleClick;
public event EventHandler<NotifyIconMouseEventArgs> MouseDown;
public event EventHandler<NotifyIconMouseEventArgs> MouseUp;
bool maped;
public bool Visible
{
get { return visible; }
set
{
//System.Threading.Thread.Sleep(10000);
visible = value;
if (visible)
{
XGrabServer(DisplayHandle);
SystrayMgrWindow = XGetSelectionOwner(DisplayHandle, LinuxPlatform.Platform.Info.Atoms._NET_SYSTEM_TRAY_S);
XUngrabServer(DisplayHandle);
XFlush(DisplayHandle);
if (SystrayMgrWindow != IntPtr.Zero)
{
//XSelectInput(DisplayHandle, SystrayMgrWindow, (IntPtr)XEventMask.StructureNotifyMask);
XSizeHints size_hints;
// We are going to be directly mapped by the system tray, so mark as mapped
// so we can later properly unmap it.
//XWindowAttributes attributes = new XWindowAttributes();
//XGetWindowAttributes(x11info.Display, XDefaultRootWindow(x11info.Display), ref attributes);
//Console.WriteLine(attributes.width + "," + attributes.height);
size_hints = new XSizeHints();
size_hints.flags = (IntPtr)(XSizeHintsFlags.PMinSize | XSizeHintsFlags.PMaxSize | XSizeHintsFlags.PBaseSize);
size_hints.min_width = 16;
size_hints.min_height = 16;
size_hints.max_width = 16;
size_hints.max_height = 16;
size_hints.base_width = 16;
size_hints.base_height = 16;
XSetWMNormalHints(DisplayHandle, Handle, ref size_hints);
IntPtr[] atoms = new IntPtr[2];
atoms[0] = (IntPtr)1; // Version 1
atoms[1] = (IntPtr)1; // we want to be mapped
XChangeProperty(DisplayHandle, Handle, LinuxPlatform.Platform.Info.Atoms._XEMBED_INFO, LinuxPlatform.Platform.Info.Atoms._XEMBED_INFO, 32, PropertyMode.Replace, atoms, 2);
SendNetClientMessage(SystrayMgrWindow, LinuxPlatform.Platform.Info.Atoms._NET_SYSTEM_TRAY_OPCODE, IntPtr.Zero, (IntPtr)SystrayRequest.SYSTEM_TRAY_REQUEST_DOCK, Handle);
//XReparentWindow(DisplayHandle, Handle, SystrayMgrWindow, 0, 0);
Invalidate();
if (maped)
{
XMapWindow(x11info.Display, Handle);
}
maped = true;
}
}
else
{
XUnmapWindow(DisplayHandle, Handle);
}
}
}
void SendNetClientMessage(IntPtr window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2)
{
XEvent xev;
xev = new XEvent();
xev.ClientMessageEvent.type = XEventName.ClientMessage;
xev.ClientMessageEvent.send_event = true;
xev.ClientMessageEvent.window = window;
xev.ClientMessageEvent.message_type = message_type;
xev.ClientMessageEvent.format = 32;
xev.ClientMessageEvent.ptr1 = l0;
xev.ClientMessageEvent.ptr2 = l1;
xev.ClientMessageEvent.ptr3 = l2;
XSendEvent(DisplayHandle, window, false, new IntPtr((int)EventMask.NoEventMask), ref xev);
//XSync(DisplayHandle, false);
}
protected override void Dispose(bool disposing)
{
if (bitmap != null)
{
bitmap.Dispose();
bitmap = null;
}
base.Dispose(disposing);
}
}
}

167
CPF.Linux/OpenGL/Glx.cs Normal file
View File

@ -0,0 +1,167 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace CPF.Linux.OpenGL
{
internal class Glx
{
private const string libGL = "libGL";
public const int None = 0;
public const int True = 1;
public const int False = 0;
public const int GL_TEXTURE_2D = 0x0DE1;
public const int GL_UNSIGNED_BYTE = 0x1401;
public const int GL_RGBA = 0x1908;
public const int GL_RGBA8 = 0x8058;
public const int GLX_USE_GL = 1;
public const int GLX_BUFFER_SIZE = 2;
public const int GLX_LEVEL = 3;
public const int GLX_RGBA = 4;
public const int GLX_DOUBLEBUFFER = 5;
public const int GLX_STEREO = 6;
public const int GLX_AUX_BUFFERS = 7;
public const int GLX_RED_SIZE = 8;
public const int GLX_GREEN_SIZE = 9;
public const int GLX_BLUE_SIZE = 10;
public const int GLX_ALPHA_SIZE = 11;
public const int GLX_DEPTH_SIZE = 12;
public const int GLX_STENCIL_SIZE = 13;
public const int GLX_ACCUM_RED_SIZE = 14;
public const int GLX_ACCUM_GREEN_SIZE = 15;
public const int GLX_ACCUM_BLUE_SIZE = 16;
public const int GLX_ACCUM_ALPHA_SIZE = 17;
public const int GLX_DRAWABLE_TYPE = 0x8010;
public const int GLX_RENDER_TYPE = 0x8011;
public const int GLX_X_RENDERABLE = 0x8012;
public const int GLX_RGBA_TYPE = 0x8014;
public const int GLX_COLOR_INDEX_TYPE = 0x8015;
public const int GLX_WINDOW_BIT = 0x00000001;
public const int GLX_PIXMAP_BIT = 0x00000002;
public const int GLX_PBUFFER_BIT = 0x00000004;
public const int GLX_RGBA_BIT = 0x00000001;
public const int GLX_SAMPLE_BUFFERS = 0x186a0;
public const int GLX_SAMPLES = 0x186a1;
public const int GLX_CONTEXT_DEBUG_BIT_ARB = 0x00000001;
public const int GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB = 0x00000002;
public const int GLX_CONTEXT_MAJOR_VERSION_ARB = 0x2091;
public const int GLX_CONTEXT_MINOR_VERSION_ARB = 0x2092;
public const int GLX_CONTEXT_FLAGS_ARB = 0x2094;
public const int GLX_CONTEXT_CORE_PROFILE_BIT_ARB = 0x00000001;
public const int GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB = 0x00000002;
public const int GLX_CONTEXT_PROFILE_MASK_ARB = 0x9126;
static Glx()
{
var ptr = glXGetProcAddressARB("glXCreateContextAttribsARB");
if (ptr != IntPtr.Zero) {
glXCreateContextAttribsARB = (glXCreateContextAttribsARBDelegate)Marshal.GetDelegateForFunctionPointer(ptr, typeof(glXCreateContextAttribsARBDelegate));
}
}
[DllImport(libGL)]
public static extern void glGetIntegerv(int name, out int rv);
[DllImport(libGL)]
public extern static bool glXQueryVersion(IntPtr dpy, out int maj, out int min);
[DllImport(libGL)]
public extern static IntPtr glXChooseFBConfig(IntPtr dpy, int screen, [In] int[] attribList, out int nitems);
public static IntPtr[] ChooseFBConfig(IntPtr dpy, int screen, int[] attribList)
{
int nitems;
var fbcArrayPtr = glXChooseFBConfig(dpy, screen, attribList, out nitems);
var fbcArray = new IntPtr[nitems];
Marshal.Copy(fbcArrayPtr, fbcArray, 0, nitems);
XLib.XFree(fbcArrayPtr);
return fbcArray;
}
[DllImport(libGL)]
public extern static IntPtr glXGetVisualFromFBConfig(IntPtr dpy, IntPtr config);
public static XVisualInfo GetVisualFromFBConfig(IntPtr dpy, IntPtr config)
{
var visualPtr = glXGetVisualFromFBConfig(dpy, config);
if (visualPtr == IntPtr.Zero) {
throw new Exception("Failed to retrieve visual from framebuffer config.");
}
var visual = (XVisualInfo) Marshal.PtrToStructure(visualPtr, typeof(XVisualInfo));
XLib.XFree(visualPtr);
return visual;
}
[DllImport(libGL)]
public extern static IntPtr glXGetCurrentContext();
[DllImport(libGL)]
public extern static IntPtr glXGetCurrentDisplay();
[DllImport(libGL)]
public extern static IntPtr glXGetCurrentDrawable();
[DllImport(libGL)]
public extern static IntPtr glXGetCurrentReadDrawable();
[DllImport(libGL)]
public extern static bool glXMakeCurrent(IntPtr dpy, IntPtr drawable, IntPtr ctx);
[DllImport(libGL)]
public extern static bool glXSwapBuffers(IntPtr dpy, IntPtr drawable);
[DllImport(libGL)]
public extern static bool glXIsDirect(IntPtr dpy, IntPtr ctx);
[DllImport(libGL)]
public extern static int glXGetFBConfigAttrib(IntPtr dpy, IntPtr config, int attribute, out int value);
[DllImport(libGL)]
public extern static IntPtr glXCreateGLXPixmap(IntPtr dpy, ref XVisualInfo visual, IntPtr pixmap);
[DllImport(libGL)]
public extern static void glXDestroyGLXPixmap(IntPtr dpy, IntPtr pixmap);
[DllImport(libGL)]
public extern static void glXDestroyContext(IntPtr dpy, IntPtr ctx);
[DllImport(libGL)]
public extern static IntPtr glXQueryExtensionsString(IntPtr dpy, int screen);
public static string QueryExtensionsString(IntPtr dpy, int screen)
{
return Marshal.PtrToStringAnsi(glXQueryExtensionsString(dpy, screen));
}
public static string[] QueryExtensions(IntPtr dpy, int screen)
{
var str = QueryExtensionsString(dpy, screen);
if (string.IsNullOrEmpty(str)) {
return new string[0];
}
return str.Split(' ');
}
[DllImport(libGL)]
public static extern IntPtr glXGetProcAddress(string buffer);
[DllImport(libGL)]
public extern static IntPtr glXGetProcAddressARB(string procname);
[DllImport(libGL)]
public extern static IntPtr glXCreateNewContext(IntPtr dpy, IntPtr config, int renderType, IntPtr shareList, int direct);
public static readonly glXCreateContextAttribsARBDelegate glXCreateContextAttribsARB;
public delegate IntPtr glXCreateContextAttribsARBDelegate(IntPtr dpy, IntPtr config, IntPtr share_context, int direct, int[] attrib_list);
[DllImport(libGL)]
public static extern void glGenTextures(int n, uint[] textures);
[DllImport(libGL)]
public static extern void glDeleteTextures(int n, uint[] textures);
[DllImport(libGL)]
public static extern void glBindTexture(uint target, uint texture);
[DllImport(libGL)]
public static extern void glTexImage2D(uint target, int level, int internalformat, int width, int height, int border, uint format, uint type, IntPtr pixels);
static IntPtr cglLib;
public static IntPtr GetProcAddress(string name)
{
if (cglLib == IntPtr.Zero)
{
cglLib =XLib. dlopen(libGL, 1);
}
return XLib.dlsym(cglLib, name);
}
}
}

View File

@ -0,0 +1,170 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using CPF.Drawing;
using SkiaSharp;
namespace CPF.Linux.OpenGL
{
internal class GlxContext : CPF.OpenGL.IGlContext
{
private IntPtr fDisplay;
//private IntPtr fPixmap;
//private IntPtr fGlxPixmap;
private IntPtr fContext;
IntPtr handle;
public GlxContext(X11Window window)
{
this.handle = window.Handle;
//fDisplay = XLib.XOpenDisplay(IntPtr.Zero);
//if (fDisplay == IntPtr.Zero) {
// Dispose();
// throw new Exception("Failed to open X display.");
//}
fDisplay = LinuxPlatform.Platform.Display;
var visualAttribs = new[] {
Glx.GLX_X_RENDERABLE, Glx.True,
Glx.GLX_DRAWABLE_TYPE, Glx.GLX_WINDOW_BIT | Glx.GLX_PBUFFER_BIT,
Glx.GLX_RENDER_TYPE, Glx.GLX_RGBA_BIT,
Glx.GLX_DOUBLEBUFFER, Glx.True,
Glx.GLX_RED_SIZE, 8,
Glx.GLX_GREEN_SIZE, 8,
Glx.GLX_BLUE_SIZE, 8,
Glx.GLX_ALPHA_SIZE, 8,
Glx.GLX_DEPTH_SIZE, 1,
Glx.GLX_STENCIL_SIZE, 8,
// Glx.GLX_SAMPLE_BUFFERS, 1,
// Glx.GLX_SAMPLES, 4,
Glx.None
};
try
{
int glxMajor, glxMinor;
if (!Glx.glXQueryVersion(fDisplay, out glxMajor, out glxMinor) ||
(glxMajor < 1) ||
(glxMajor == 1 && glxMinor < 3))
{
Console.WriteLine($"GLX version 1.3 or higher required ({glxMajor}.{glxMinor} provided).");
return;
}
var fbc = Glx.ChooseFBConfig(fDisplay, LinuxPlatform.Platform.Info.DefaultScreen, visualAttribs);
if (fbc.Length == 0)
{
Console.WriteLine("Failed to retrieve a framebuffer config.");
return;
}
var bestFBC = IntPtr.Zero;
var bestNumSamp = -1;
for (int i = 0; i < fbc.Length; i++)
{
int sampleBuf, samples;
Glx.glXGetFBConfigAttrib(fDisplay, fbc[i], Glx.GLX_SAMPLE_BUFFERS, out sampleBuf);
Glx.glXGetFBConfigAttrib(fDisplay, fbc[i], Glx.GLX_SAMPLES, out samples);
var visual = Glx.GetVisualFromFBConfig(fDisplay, fbc[i]);
if (bestFBC == IntPtr.Zero || (sampleBuf > 0 && samples > bestNumSamp || visual.depth == 32))
{
bestFBC = fbc[i];
bestNumSamp = samples;
}
}
fContext = Glx.glXCreateNewContext(fDisplay, bestFBC, Glx.GLX_RGBA_TYPE, IntPtr.Zero, Glx.True);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
public IntPtr Context { get { return fContext; } }
IntPtr oldHandle;
IntPtr oldDisplay;
IntPtr oldContext;
public void MakeCurrent()
{
oldContext = Glx.glXGetCurrentContext();
oldDisplay = Glx.glXGetCurrentDisplay();
oldHandle = Glx.glXGetCurrentDrawable();
if (!Glx.glXMakeCurrent(fDisplay, handle, fContext))
{
Dispose();
throw new Exception("Failed to set the context.");
}
}
public void SwapBuffers()
{
Glx.glXSwapBuffers(fDisplay, handle);
Glx.glXMakeCurrent(oldDisplay, oldHandle, oldContext);
}
public void Dispose()
{
if (fDisplay != IntPtr.Zero)
{
//Glx.glXMakeCurrent(fDisplay, IntPtr.Zero, IntPtr.Zero);
if (fContext != IntPtr.Zero)
{
Glx.glXDestroyContext(fDisplay, fContext);
fContext = IntPtr.Zero;
}
//if (fGlxPixmap != IntPtr.Zero) {
// Glx.glXDestroyGLXPixmap(fDisplay, fGlxPixmap);
// fGlxPixmap = IntPtr.Zero;
//}
//if (fPixmap != IntPtr.Zero) {
// XLib.XFreePixmap(fDisplay, fPixmap);
// fPixmap = IntPtr.Zero;
//}
fDisplay = IntPtr.Zero;
}
GRContext?.Dispose();
GRContext = null;
}
public IntPtr GetProcAddress(string name)
{
return Glx.glXGetProcAddressARB(name);
}
//public override GRGlTextureInfo CreateTexture(SKSizeI textureSize)
//{
// var textures = new uint[1];
// Glx.glGenTextures(textures.Length, textures);
// var textureId = textures[0];
// Glx.glBindTexture(Glx.GL_TEXTURE_2D, textureId);
// Glx.glTexImage2D(Glx.GL_TEXTURE_2D, 0, Glx.GL_RGBA, textureSize.Width, textureSize.Height, 0, Glx.GL_RGBA, Glx.GL_UNSIGNED_BYTE, IntPtr.Zero);
// Glx.glBindTexture(Glx.GL_TEXTURE_2D, 0);
// return new GRGlTextureInfo {
// Id = textureId,
// Target = Glx.GL_TEXTURE_2D,
// Format = Glx.GL_RGBA8
// };
//}
//public override void DestroyTexture(uint texture)
//{
// Glx.glDeleteTextures(1, new[] { texture });
//}
public const int GL_FRAMEBUFFER_BINDING = 0x8CA6;
public IDisposable GRContext { get; set; }
public void GetFramebufferInfo(out int framebuffer, out int samples, out int stencil)
{
Glx.glGetIntegerv(GL_FRAMEBUFFER_BINDING, out framebuffer);
Glx.glGetIntegerv(3415, out stencil);
Glx.glGetIntegerv(32937, out samples);
}
}
}

355
CPF.Linux/ScreenImpl.cs Normal file
View File

@ -0,0 +1,355 @@
using System;
using System.Collections.Generic;
using System.Text;
using CPF.Drawing;
using CPF.Platform;
using System.Globalization;
using System.Linq;
using static CPF.Linux.XLib;
using System.Runtime.InteropServices;
namespace CPF.Linux
{
public class ScreenImpl : Screen
{
public ScreenImpl(Rect bounds, Rect workingArea, bool primary,
string name, Size? physicalSize, double? pixelDensity) : base(bounds, workingArea, primary)
{
XWorkingArea = workingArea;
Name = name;
if (physicalSize == null && pixelDensity == null)
{
PixelDensity = 1;
}
else if (pixelDensity == null)
{
PixelDensity = GuessPixelDensity(bounds.Width, physicalSize.Value.Width);
}
else
{
PixelDensity = pixelDensity.Value;
PhysicalSize = physicalSize;
}
}
private const int FullHDWidth = 1920;
public string Name { get; set; }
public Size? PhysicalSize { get; set; }
public double PixelDensity { get; set; }
public Rect XWorkingArea { get; set; }
public override Bitmap Screenshot()
{
var bounds = Bounds;
var image = XGetImage(LinuxPlatform.Platform.Display, LinuxPlatform.Platform.Info.RootWindow, (int)bounds.X, (int)bounds.Y, (int)bounds.Width,
(int)bounds.Height, ~0, 2 /* ZPixmap*/);
if (image == IntPtr.Zero)
{
string s = String.Format("XGetImage returned NULL when asked to for a {0}x{1} region block",
bounds.Width, bounds.Height);
throw new InvalidOperationException(s);
}
Bitmap bmp = new Bitmap((int)bounds.Width, (int)bounds.Height);
var visual = LinuxPlatform.Platform.Info.TransparentVisualInfo;
int red, blue, green;
int red_mask = (int)visual.red_mask;
int blue_mask = (int)visual.blue_mask;
int green_mask = (int)visual.green_mask;
using (var b = bmp.Lock())
{
for (int y = 0; y < bounds.Height; y++)
{
for (int x = 0; x < bounds.Width; x++)
{
var pixel = XGetPixel(image, x, y);
switch (visual.depth)
{
case 16: /* 16bbp pixel transformation */
red = (int)((pixel & red_mask) >> 8) & 0xff;
green = (int)(((pixel & green_mask) >> 3)) & 0xff;
blue = (int)((pixel & blue_mask) << 3) & 0xff;
break;
case 24:
case 32:
red = (int)((pixel & red_mask) >> 16) & 0xff;
green = (int)(((pixel & green_mask) >> 8)) & 0xff;
blue = (int)((pixel & blue_mask)) & 0xff;
break;
default:
string text = string.Format("{0}bbp depth not supported.", visual.depth);
throw new NotImplementedException(text);
}
b.SetPixel(x, y, 255, (byte)red, (byte)green, (byte)blue);
}
}
}
XDestroyImage(image);
return bmp;
}
public static double GuessPixelDensity(double pixelWidth, double mmWidth)
=> pixelWidth <= FullHDWidth ? 1 : Math.Max(1, Math.Round(pixelWidth / mmWidth * 25.4 / 96));
//private static X11ScreensUserSettings _settings;
private static ScreenImpl[] _cache;
private static X11Info _x11;
private static XWindow _window;
//用来解决虚拟机里调整屏幕分辨率之后缓存读取到屏幕尺寸不对的问题
static byte isScreenChange;
static ScreenImpl()
{
//_settings = X11ScreensUserSettings.Detect();
_x11 = LinuxPlatform.Platform.Info;
_window = new XWindow
{
EventAction = (ref XEvent ev) =>
{
// Invalidate cache on RRScreenChangeNotify
if ((int)ev.type == _x11.RandrEventBase + (int)RandrEvent.RRScreenChangeNotify)
{
_cache = null;
isScreenChange = 3;
}
}
};
XRRSelectInput(_x11.Display, _window.Handle, RandrEventMask.RRScreenChangeNotify);
}
public static unsafe ScreenImpl[] Screens
{
get
{
if (_cache != null && isScreenChange > 0)
{
isScreenChange--;
_cache = null;
}
if (_cache != null)
return _cache;
//Console.WriteLine("Screens");
var monitors = XRRGetMonitors(_x11.Display, _window.Handle, true, out var count);
//Console.WriteLine("Screens:" + count);
if (count > 0)
{
var screens = new ScreenImpl[count];
for (var c = 0; c < count; c++)
{
var mon = monitors[c];
var namePtr = XGetAtomName(_x11.Display, mon.Name);
var name = Marshal.PtrToStringAnsi(namePtr);
XFree(namePtr);
var density = 1d;
//if (_settings.NamedScaleFactors?.TryGetValue(name, out density) != true)
{
if (mon.MWidth == 0)
density = 1;
else
density = ScreenImpl.GuessPixelDensity(mon.Width, mon.MWidth);
}
//for (int o = 0; o < mon.NOutput; o++)
//{
// Console.WriteLine(GetPhysicalMonitorSizeFromEDID(mon.Outputs[o]));
//}
//density *= _settings.GlobalScaleFactor;
var bounds = new Rect(mon.X, mon.Y, mon.Width, mon.Height);
screens[c] = new ScreenImpl(bounds, bounds,
mon.Primary != 0,
name,
(mon.MWidth == 0 || mon.MHeight == 0) ? (Size?)null : new Size(mon.MWidth, mon.MHeight),
density);
}
_cache = UpdateWorkArea(_x11, screens);
}
else
{
Console.WriteLine("可能无法显示界面,请检查显卡驱动以及开启桌面混合");
_cache = new ScreenImpl[1] { new ScreenImpl(new Rect(0, 0, 1024, 768), new Rect(0, 0, 1024, 768), true, "默认", null, null) { } };
}
GetDpi();
XFree(new IntPtr(monitors));
return _cache;
}
}
public static unsafe void GetDpi()
{
var resourceString = XResourceManagerString(LinuxPlatform.Platform.Display);
IntPtr db;
XrmValue value = new XrmValue();
IntPtr type = IntPtr.Zero;
double dpi = 96;
if (resourceString != IntPtr.Zero)
{
db = XrmGetStringDatabase(resourceString);
//Console.WriteLine(resourceString);
if (XrmGetResource(db, "Xft.dpi", "String", ref type, ref value))
{
if (value.addr != null)
{
dpi = atof(value.addr);
Console.WriteLine("DPI:" + dpi);
}
}
}
else
{
System.Diagnostics.Debug.WriteLine("XResourceManagerString读取失败");
}
var scale = (float)dpi / 96;
if (scale != DpiScale)
{
DpiScale = scale;
foreach (var item in LinuxPlatform.Platform.windows)
{
if (item.Value is X11Window window)
{
window.UpdateScaling();
}
}
}
}
public static float DpiScale = 1;
//const int EDIDStructureLength = 32; // Length of a EDID-Block-Length(128 bytes), XRRGetOutputProperty multiplies offset and length by 4
//private static unsafe Size? GetPhysicalMonitorSizeFromEDID(IntPtr rrOutput)
//{
// if (rrOutput == IntPtr.Zero)
// return null;
// var properties = XLib.XRRListOutputProperties(_x11.Display, rrOutput, out int propertyCount);
// var hasEDID = false;
// for (var pc = 0; pc < propertyCount; pc++)
// {
// if (properties[pc] == _x11.Atoms.EDID)
// hasEDID = true;
// }
// if (!hasEDID)
// return null;
// XLib.XRRGetOutputProperty(_x11.Display, rrOutput, _x11.Atoms.EDID, 0, EDIDStructureLength, false, false, _x11.Atoms.AnyPropertyType, out IntPtr actualType, out int actualFormat, out int bytesAfter, out _, out IntPtr prop);
// if (actualType != _x11.Atoms.XA_INTEGER)
// return null;
// if (actualFormat != 8) // Expecting an byte array
// return null;
// var edid = new byte[bytesAfter];
// Marshal.Copy(prop, edid, 0, bytesAfter);
// XFree(prop);
// XFree(new IntPtr(properties));
// if (edid.Length < 22)
// return null;
// var width = edid[21]; // 0x15 1 Max. Horizontal Image Size cm.
// var height = edid[22]; // 0x16 1 Max. Vertical Image Size cm.
// if (width == 0 && height == 0)
// return null;
// return new Size(width * 10, height * 10);
//}
static unsafe ScreenImpl[] UpdateWorkArea(X11Info info, ScreenImpl[] screens)
{
var rect = default(Rect);
foreach (var s in screens)
{
rect.Union(s.Bounds);
//Fallback value
s.XWorkingArea = s.Bounds;
}
var res = XGetWindowProperty(info.Display,
info.RootWindow,
info.Atoms._NET_WORKAREA,
IntPtr.Zero,
new IntPtr(128),
false,
info.Atoms.AnyPropertyType,
out var type,
out var format,
out var count,
out var bytesAfter,
out var prop);
if (res != (int)Status.Success || type == IntPtr.Zero ||
format == 0 || bytesAfter.ToInt64() != 0 || count.ToInt64() % 4 != 0)
{ return screens.Select(a => new ScreenImpl(a.Bounds, a.XWorkingArea, a.Primary, a.Name, a.PhysicalSize, a.PixelDensity)).ToArray(); }
var pwa = (IntPtr*)prop;
var wa = new Rect(pwa[0].ToInt32(), pwa[1].ToInt32(), pwa[2].ToInt32(), pwa[3].ToInt32());
foreach (var s in screens)
{
var b = s.Bounds;
b.Intersect(wa);
s.XWorkingArea = b;
}
XFree(prop);
return screens.Select(a => new ScreenImpl(a.Bounds, a.XWorkingArea, a.Primary, a.Name, a.PhysicalSize, a.PixelDensity)).ToArray();
}
}
//class X11ScreensUserSettings
//{
// public double GlobalScaleFactor { get; set; } = 1;
// public Dictionary<string, double> NamedScaleFactors { get; set; }
// static double? TryParse(string s)
// {
// if (s == null)
// return null;
// if (double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out var rv))
// return rv;
// return null;
// }
// public static X11ScreensUserSettings DetectEnvironment()
// {
// var globalFactor = Environment.GetEnvironmentVariable("CPF_GLOBAL_SCALE_FACTOR");
// var screenFactors = Environment.GetEnvironmentVariable("CPF_SCREEN_SCALE_FACTORS");
// if (globalFactor == null && screenFactors == null)
// return null;
// var rv = new X11ScreensUserSettings
// {
// GlobalScaleFactor = TryParse(globalFactor) ?? 1
// };
// try
// {
// if (!string.IsNullOrWhiteSpace(screenFactors))
// {
// rv.NamedScaleFactors = screenFactors.Split(';').Where(x => !string.IsNullOrWhiteSpace(x))
// .Select(x => x.Split('=')).ToDictionary(x => x[0],
// x => double.Parse(x[1], CultureInfo.InvariantCulture));
// }
// }
// catch
// {
// //Ignore
// }
// return rv;
// }
// public static X11ScreensUserSettings Detect()
// {
// return DetectEnvironment() ?? new X11ScreensUserSettings();
// }
//}
}

200
CPF.Linux/X11Atoms.cs Normal file
View File

@ -0,0 +1,200 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
namespace CPF.Linux
{
public class X11Atoms
{
// Our atoms
public readonly IntPtr AnyPropertyType = (IntPtr)0;
public readonly IntPtr XA_PRIMARY = (IntPtr)1;
public readonly IntPtr XA_SECONDARY = (IntPtr)2;
public readonly IntPtr XA_ARC = (IntPtr)3;
public readonly IntPtr XA_ATOM = (IntPtr)4;
public readonly IntPtr XA_BITMAP = (IntPtr)5;
public readonly IntPtr XA_CARDINAL = (IntPtr)6;
public readonly IntPtr XA_COLORMAP = (IntPtr)7;
public readonly IntPtr XA_CURSOR = (IntPtr)8;
public readonly IntPtr XA_CUT_BUFFER0 = (IntPtr)9;
public readonly IntPtr XA_CUT_BUFFER1 = (IntPtr)10;
public readonly IntPtr XA_CUT_BUFFER2 = (IntPtr)11;
public readonly IntPtr XA_CUT_BUFFER3 = (IntPtr)12;
public readonly IntPtr XA_CUT_BUFFER4 = (IntPtr)13;
public readonly IntPtr XA_CUT_BUFFER5 = (IntPtr)14;
public readonly IntPtr XA_CUT_BUFFER6 = (IntPtr)15;
public readonly IntPtr XA_CUT_BUFFER7 = (IntPtr)16;
public readonly IntPtr XA_DRAWABLE = (IntPtr)17;
public readonly IntPtr XA_FONT = (IntPtr)18;
public readonly IntPtr XA_INTEGER = (IntPtr)19;
public readonly IntPtr XA_PIXMAP = (IntPtr)20;
public readonly IntPtr XA_POINT = (IntPtr)21;
public readonly IntPtr XA_RECTANGLE = (IntPtr)22;
public readonly IntPtr XA_RESOURCE_MANAGER = (IntPtr)23;
public readonly IntPtr XA_RGB_COLOR_MAP = (IntPtr)24;
public readonly IntPtr XA_RGB_BEST_MAP = (IntPtr)25;
public readonly IntPtr XA_RGB_BLUE_MAP = (IntPtr)26;
public readonly IntPtr XA_RGB_DEFAULT_MAP = (IntPtr)27;
public readonly IntPtr XA_RGB_GRAY_MAP = (IntPtr)28;
public readonly IntPtr XA_RGB_GREEN_MAP = (IntPtr)29;
public readonly IntPtr XA_RGB_RED_MAP = (IntPtr)30;
public readonly IntPtr XA_STRING = (IntPtr)31;
public readonly IntPtr XA_VISUALID = (IntPtr)32;
public readonly IntPtr XA_WINDOW = (IntPtr)33;
public readonly IntPtr XA_WM_COMMAND = (IntPtr)34;
public readonly IntPtr XA_WM_HINTS = (IntPtr)35;
public readonly IntPtr XA_WM_CLIENT_MACHINE = (IntPtr)36;
public readonly IntPtr XA_WM_ICON_NAME = (IntPtr)37;
public readonly IntPtr XA_WM_ICON_SIZE = (IntPtr)38;
public readonly IntPtr XA_WM_NAME = (IntPtr)39;
public readonly IntPtr XA_WM_NORMAL_HINTS = (IntPtr)40;
public readonly IntPtr XA_WM_SIZE_HINTS = (IntPtr)41;
public readonly IntPtr XA_WM_ZOOM_HINTS = (IntPtr)42;
public readonly IntPtr XA_MIN_SPACE = (IntPtr)43;
public readonly IntPtr XA_NORM_SPACE = (IntPtr)44;
public readonly IntPtr XA_MAX_SPACE = (IntPtr)45;
public readonly IntPtr XA_END_SPACE = (IntPtr)46;
public readonly IntPtr XA_SUPERSCRIPT_X = (IntPtr)47;
public readonly IntPtr XA_SUPERSCRIPT_Y = (IntPtr)48;
public readonly IntPtr XA_SUBSCRIPT_X = (IntPtr)49;
public readonly IntPtr XA_SUBSCRIPT_Y = (IntPtr)50;
public readonly IntPtr XA_UNDERLINE_POSITION = (IntPtr)51;
public readonly IntPtr XA_UNDERLINE_THICKNESS = (IntPtr)52;
public readonly IntPtr XA_STRIKEOUT_ASCENT = (IntPtr)53;
public readonly IntPtr XA_STRIKEOUT_DESCENT = (IntPtr)54;
public readonly IntPtr XA_ITALIC_ANGLE = (IntPtr)55;
public readonly IntPtr XA_X_HEIGHT = (IntPtr)56;
public readonly IntPtr XA_QUAD_WIDTH = (IntPtr)57;
public readonly IntPtr XA_WEIGHT = (IntPtr)58;
public readonly IntPtr XA_POINT_SIZE = (IntPtr)59;
public readonly IntPtr XA_RESOLUTION = (IntPtr)60;
public readonly IntPtr XA_COPYRIGHT = (IntPtr)61;
public readonly IntPtr XA_NOTICE = (IntPtr)62;
public readonly IntPtr XA_FONT_NAME = (IntPtr)63;
public readonly IntPtr XA_FAMILY_NAME = (IntPtr)64;
public readonly IntPtr XA_FULL_NAME = (IntPtr)65;
public readonly IntPtr XA_CAP_HEIGHT = (IntPtr)66;
public readonly IntPtr XA_WM_CLASS = (IntPtr)67;
public readonly IntPtr XA_WM_TRANSIENT_FOR = (IntPtr)68;
public readonly IntPtr WM_PROTOCOLS;
public readonly IntPtr WM_DELETE_WINDOW;
public readonly IntPtr WM_TAKE_FOCUS;
public readonly IntPtr _NET_SUPPORTED;
public readonly IntPtr _NET_CLIENT_LIST;
public readonly IntPtr _NET_NUMBER_OF_DESKTOPS;
public readonly IntPtr _NET_DESKTOP_GEOMETRY;
public readonly IntPtr _NET_DESKTOP_VIEWPORT;
public readonly IntPtr _NET_CURRENT_DESKTOP;
public readonly IntPtr _NET_DESKTOP_NAMES;
public readonly IntPtr _NET_ACTIVE_WINDOW;
public readonly IntPtr _NET_WORKAREA;
public readonly IntPtr _NET_SUPPORTING_WM_CHECK;
public readonly IntPtr _NET_VIRTUAL_ROOTS;
public readonly IntPtr _NET_DESKTOP_LAYOUT;
public readonly IntPtr _NET_SHOWING_DESKTOP;
public readonly IntPtr _NET_CLOSE_WINDOW;
public readonly IntPtr _NET_MOVERESIZE_WINDOW;
public readonly IntPtr _NET_WM_MOVERESIZE;
public readonly IntPtr _NET_RESTACK_WINDOW;
public readonly IntPtr _NET_REQUEST_FRAME_EXTENTS;
public readonly IntPtr _NET_WM_NAME;
public readonly IntPtr _NET_WM_VISIBLE_NAME;
public readonly IntPtr _NET_WM_ICON_NAME;
public readonly IntPtr _NET_WM_VISIBLE_ICON_NAME;
public readonly IntPtr _NET_WM_DESKTOP;
public readonly IntPtr _NET_WM_WINDOW_TYPE;
public readonly IntPtr _NET_WM_STATE;
public readonly IntPtr _NET_WM_ALLOWED_ACTIONS;
public readonly IntPtr _NET_WM_STRUT;
public readonly IntPtr _NET_WM_STRUT_PARTIAL;
public readonly IntPtr _NET_WM_ICON_GEOMETRY;
public readonly IntPtr _NET_WM_ICON;
public readonly IntPtr _NET_WM_PID;
public readonly IntPtr _NET_WM_HANDLED_ICONS;
public readonly IntPtr _NET_WM_USER_TIME;
public readonly IntPtr _NET_FRAME_EXTENTS;
public readonly IntPtr _NET_WM_PING;
public readonly IntPtr _NET_WM_SYNC_REQUEST;
public readonly IntPtr _NET_SYSTEM_TRAY_S;
public readonly IntPtr _NET_SYSTEM_TRAY_ORIENTATION;
public readonly IntPtr _NET_SYSTEM_TRAY_OPCODE;
public readonly IntPtr _NET_WM_STATE_MAXIMIZED_HORZ;
public readonly IntPtr _NET_WM_STATE_MAXIMIZED_VERT;
public readonly IntPtr _XEMBED;
public readonly IntPtr _XEMBED_INFO;
public readonly IntPtr _MOTIF_WM_HINTS;
public readonly IntPtr _NET_WM_STATE_SKIP_TASKBAR;
public readonly IntPtr _NET_WM_STATE_ABOVE;
public readonly IntPtr _NET_WM_STATE_MODAL;
public readonly IntPtr _NET_WM_STATE_HIDDEN;
public readonly IntPtr _NET_WM_STATE_FOCUSED;
public readonly IntPtr _NET_WM_CONTEXT_HELP;
public readonly IntPtr _NET_WM_WINDOW_OPACITY;
public readonly IntPtr _NET_WM_WINDOW_TYPE_DESKTOP;
public readonly IntPtr _NET_WM_WINDOW_TYPE_DOCK;
public readonly IntPtr _NET_WM_WINDOW_TYPE_TOOLBAR;
public readonly IntPtr _NET_WM_WINDOW_TYPE_MENU;
public readonly IntPtr _NET_WM_WINDOW_TYPE_UTILITY;
public readonly IntPtr _NET_WM_WINDOW_TYPE_SPLASH;
public readonly IntPtr _NET_WM_WINDOW_TYPE_DIALOG;
public readonly IntPtr _NET_WM_WINDOW_TYPE_NORMAL;
public readonly IntPtr _NET_WM_STATE_FULLSCREEN;
public readonly IntPtr CLIPBOARD;
public readonly IntPtr CLIPBOARD_MANAGER;
public readonly IntPtr SAVE_TARGETS;
public readonly IntPtr MULTIPLE;
public readonly IntPtr PRIMARY;
public readonly IntPtr OEMTEXT;
public readonly IntPtr UNICODETEXT;
public readonly IntPtr TARGETS;
public readonly IntPtr UTF8_STRING;
public readonly IntPtr UTF16_STRING;
public readonly IntPtr ATOM_PAIR;
public readonly IntPtr STRING;
public readonly IntPtr TEXT;
public readonly IntPtr COMPOUND_TEXT;
public readonly IntPtr XdndActionCopy;
public readonly IntPtr XdndActionMove;
public readonly IntPtr XdndActionLink;
//static IntPtr XdndActionPrivate;
public readonly IntPtr XdndActionList;
public readonly IntPtr XdndAware;
public readonly IntPtr XdndEnter;
public readonly IntPtr XdndLeave;
public readonly IntPtr XdndPosition;
public readonly IntPtr XdndStatus;
public readonly IntPtr XdndDrop;
public readonly IntPtr XdndSelection;
public readonly IntPtr XdndFinished;
public readonly IntPtr XdndTypeList;
public readonly IntPtr EDID;
//自定义消息
public static IntPtr Invoke = (IntPtr)1500;
public static IntPtr BeginInvoke = (IntPtr)1501;
public X11Atoms(IntPtr display,int screen)
{
// make sure this array stays in sync with the statements below
var fields = typeof(X11Atoms).GetFields()
.Where(f => f.FieldType == typeof(IntPtr) && (IntPtr)f.GetValue(this) == IntPtr.Zero).ToArray();
var atomNames = fields.Select(f => f.Name).ToArray();
IntPtr[] atoms = new IntPtr[atomNames.Length];
XLib.XInternAtoms(display, atomNames, atomNames.Length, true, atoms);
for (var c = 0; c < fields.Length; c++)
fields[c].SetValue(this, atoms[c]);
_NET_SYSTEM_TRAY_S = XLib.XInternAtom(display, "_NET_SYSTEM_TRAY_S" + screen, false);
}
}
}

322
CPF.Linux/X11Clipboard.cs Normal file
View File

@ -0,0 +1,322 @@
using System;
using System.Collections.Generic;
using System.Text;
using CPF.Input;
using System.Threading.Tasks;
using System.Linq;
using static CPF.Linux.XLib;
using System.Runtime.InteropServices;
namespace CPF.Linux
{
class X11Clipboard : IClipboard
{
private readonly X11Info _x11;
private string _storedString;
private string _storedHtml;
private XWindow window;
private CancelHandle _requestedFormatsHandle;
private CancelHandle _requestedTextHandle;
private readonly IntPtr[] _textAtoms;
public X11Clipboard()
{
_x11 = LinuxPlatform.Platform.Info;
window = new XWindow { EventAction = OnXEvent };
//_saveTargetsAtom = XInternAtom(_x11.Display, "PENGUIN", false);
_textAtoms = new[]
{
_x11.Atoms.XA_STRING,
_x11.Atoms.OEMTEXT,
_x11.Atoms.UTF8_STRING,
_x11.Atoms.UTF16_STRING
}.Where(a => a != IntPtr.Zero).ToArray();
}
void OnXEvent(ref XEvent ev)
{
if (ev.type == XEventName.SelectionRequest)
{
var sel = ev.SelectionRequestEvent;
var resp = new XEvent
{
SelectionEvent =
{
type = XEventName.SelectionNotify,
send_event = true,
display = _x11.Display,
selection = sel.selection,
target = sel.target,
requestor = sel.requestor,
time = sel.time,
property = IntPtr.Zero
}
};
if (sel.selection == _x11.Atoms.CLIPBOARD)
{
resp.SelectionEvent.property = WriteTargetToProperty(sel.target, sel.requestor, sel.property);
}
XSendEvent(_x11.Display, sel.requestor, false, new IntPtr((int)EventMask.NoEventMask), ref resp);
XFlush(_x11.Display);
}
}
Encoding GetStringEncoding(IntPtr atom)
{
return (atom == _x11.Atoms.XA_STRING
|| atom == _x11.Atoms.OEMTEXT)
? Encoding.ASCII
: atom == _x11.Atoms.UTF8_STRING
? Encoding.UTF8
: atom == _x11.Atoms.UTF16_STRING
? Encoding.Unicode
: null;
}
unsafe IntPtr WriteTargetToProperty(IntPtr target, IntPtr window, IntPtr property)
{
Encoding textEnc;
if (target == _x11.Atoms.TARGETS)
{
var atoms = _textAtoms;
atoms = atoms.Concat(new[] { _x11.Atoms.TARGETS, _x11.Atoms.MULTIPLE })
.ToArray();
if (!string.IsNullOrEmpty(_storedHtml))
{
atoms = atoms.Concat(new[] { DataObject.htmlAtom })
.ToArray();
}
XChangeProperty(_x11.Display, window, property, _x11.Atoms.XA_ATOM, 32, PropertyMode.Replace, atoms, atoms.Length);
return property;
}
else if (target == _x11.Atoms.SAVE_TARGETS && _x11.Atoms.SAVE_TARGETS != IntPtr.Zero)
{
return property;
}
else if ((textEnc = GetStringEncoding(target)) != null)
{
var data = textEnc.GetBytes(_storedString ?? "");
//var ptr = Marshal.AllocHGlobal(data.Length);
//Marshal.Copy(data, 0, ptr, data.Length);
//XChangeProperty(_x11.Display, window, property, target, 8,
// PropertyMode.Replace,
// (void*)ptr, data.Length);
fixed (void* pdata = data)
XChangeProperty(_x11.Display, window, property, target, 8,
PropertyMode.Replace,
pdata, data.Length);
return property;
}
else if (target == _x11.Atoms.MULTIPLE && _x11.Atoms.MULTIPLE != IntPtr.Zero)
{
XGetWindowProperty(_x11.Display, window, property, IntPtr.Zero, new IntPtr(0x7fffffff), false,
_x11.Atoms.ATOM_PAIR, out _, out var actualFormat, out var nitems, out _, out var prop);
if (nitems == IntPtr.Zero)
return IntPtr.Zero;
if (actualFormat == 32)
{
var data = (IntPtr*)prop.ToPointer();
for (var c = 0; c < nitems.ToInt32(); c += 2)
{
var subTarget = data[c];
var subProp = data[c + 1];
var converted = WriteTargetToProperty(subTarget, window, subProp);
data[c + 1] = converted;
}
XChangeProperty(_x11.Display, window, property, _x11.Atoms.ATOM_PAIR, 32, PropertyMode.Replace,
prop.ToPointer(), nitems.ToInt32());
}
XFree(prop);
return property;
}
else if (target == DataObject.htmlAtom)
{
var data = Encoding.UTF8.GetBytes(_storedHtml ?? "");
fixed (void* pdata = data)
XChangeProperty(_x11.Display, window, property, target, 8, PropertyMode.Replace, pdata, data.Length);
return property;
}
else
return IntPtr.Zero;
}
private unsafe bool OnEvent(ref XEvent ev)
{
if (ev.type == XEventName.SelectionNotify && ev.SelectionEvent.selection == _x11.Atoms.CLIPBOARD)
{
var sel = ev.SelectionEvent;
if (sel.property == IntPtr.Zero)
{
_requestedFormatsHandle.SetResult(null);
_requestedTextHandle.SetResult(null);
}
XGetWindowProperty(_x11.Display, window.Handle, sel.property, IntPtr.Zero, new IntPtr(0x7fffffff), true, (IntPtr)Atom.AnyPropertyType,
out var actualAtom, out var actualFormat, out var nitems, out var bytes_after, out var prop);
Encoding textEnc = null;
if (nitems == IntPtr.Zero)
{
_requestedFormatsHandle.SetResult(null);
_requestedTextHandle.SetResult(null);
}
else
{
if (sel.property == _x11.Atoms.TARGETS)
{
if (actualFormat != 32)
{
_requestedFormatsHandle.SetResult(null);
}
else
{
var formats = new IntPtr[nitems.ToInt32()];
Marshal.Copy(prop, formats, 0, formats.Length);
_requestedFormatsHandle.SetResult(formats);
//_requestedFormatsTcs?.TrySetResult(formats);
}
}
else if ((textEnc = GetStringEncoding(sel.property)) != null)
{
var text = textEnc.GetString((byte*)prop.ToPointer(), nitems.ToInt32());
//_requestedTextTcs?.TrySetResult(text);
_requestedTextHandle.SetResult(text);
}
else if (sel.property == DataObject.htmlAtom)
{
var html = Encoding.UTF8.GetString((byte*)prop.ToPointer(), nitems.ToInt32());
_requestedTextHandle.SetResult(html);
}
}
XFree(prop);
}
return false;
}
IntPtr[] SendFormatRequest()
{
//if (_requestedFormatsTcs == null || _requestedFormatsTcs.Task.IsCompleted)
// _requestedFormatsTcs = new TaskCompletionSource<IntPtr[]>();
if (_requestedFormatsHandle == null || _requestedFormatsHandle.Cancel)
{
_requestedFormatsHandle = new CancelHandle();
}
XConvertSelection(_x11.Display, _x11.Atoms.CLIPBOARD, _x11.Atoms.TARGETS, _x11.Atoms.TARGETS, window.Handle,
IntPtr.Zero);
//while (!_requestedFormatsTcs.Task.IsCompleted)
//{
// XNextEvent(_x11.Display, out var xev);
// OnEvent(xev);
// LinuxPlatform.Platform.OnEvent(xev);
//}
LinuxPlatform.Platform.RunMainLoop(_requestedFormatsHandle, OnEvent);
//return _requestedFormatsTcs.Task;
return (IntPtr[])_requestedFormatsHandle.Data;
}
string SendTextRequest(IntPtr format)
{
//if (_requestedTextTcs == null || _requestedFormatsTcs.Task.IsCompleted)
// _requestedTextTcs = new TaskCompletionSource<string>();
if (_requestedTextHandle == null || _requestedFormatsHandle.Cancel)
{
_requestedTextHandle = new CancelHandle();
}
XConvertSelection(_x11.Display, _x11.Atoms.CLIPBOARD, format, format, window.Handle, IntPtr.Zero);
//while (!_requestedTextTcs.Task.IsCompleted)
//{
// XNextEvent(_x11.Display, out var xev);
// OnEvent(xev);
// LinuxPlatform.Platform.OnEvent(xev);
//}
//return _requestedTextTcs.Task;
LinuxPlatform.Platform.RunMainLoop(_requestedTextHandle, OnEvent);
return (string)_requestedTextHandle.Data;
}
public void Clear()
{
SetData((DataFormat.Text, null));
}
public bool Contains(DataFormat dataFormat)
{
if (dataFormat == DataFormat.Text)
{
return GetData(DataFormat.Text) != null;
}
else if (dataFormat == DataFormat.Html)
{
return GetData(DataFormat.Html) != null;
}
return false;
}
string GetText()
{
if (XGetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD) == IntPtr.Zero)
return null;
var res = SendFormatRequest();
var target = _x11.Atoms.UTF8_STRING;
if (res != null)
{
var preferredFormats = new[] { _x11.Atoms.UTF16_STRING, _x11.Atoms.UTF8_STRING, _x11.Atoms.XA_STRING };
foreach (var pf in preferredFormats)
if (res.Contains(pf))
{
target = pf;
break;
}
}
return SendTextRequest(target);
}
string GetHtml()
{
if (XGetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD) == IntPtr.Zero)
return null;
var res = SendFormatRequest();
return SendTextRequest(DataObject.htmlAtom);
}
public object GetData(DataFormat dataFormat)
{
if (dataFormat == DataFormat.Text)
{
return GetText();
}
if (dataFormat == DataFormat.Html)
{
return GetHtml();
}
throw new NotImplementedException("暂时不支持:" + dataFormat);
}
public void SetData(params (DataFormat, object)[] data)
{
_storedHtml = null;
_storedString = null;
foreach (var item in data)
{
if (item.Item1 == DataFormat.Text)
{
_storedString = item.Item2 == null ? null : item.Item2.ToString();
//break;
}
else if (item.Item1 == DataFormat.Html)
{
_storedHtml = item.Item2?.ToString();
}
}
if (data.Length > 0)
{
XSetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD, window.Handle, IntPtr.Zero);
}
}
}
}

112
CPF.Linux/X11Enums.cs Normal file
View File

@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CPF.Linux
{
public enum Status
{
Success = 0, /* everything's okay */
BadRequest = 1, /* bad request code */
BadValue = 2, /* int parameter out of range */
BadWindow = 3, /* parameter not a Window */
BadPixmap = 4, /* parameter not a Pixmap */
BadAtom = 5, /* parameter not an Atom */
BadCursor = 6, /* parameter not a Cursor */
BadFont = 7, /* parameter not a Font */
BadMatch = 8, /* parameter mismatch */
BadDrawable = 9, /* parameter not a Pixmap or Window */
BadAccess = 10, /* depending on context:
- key/button already grabbed
- attempt to free an illegal
cmap entry
- attempt to store into a read-only
color map entry.
- attempt to modify the access control
list from other than the local host.
*/
BadAlloc = 11, /* insufficient resources */
BadColor = 12, /* no such colormap */
BadGC = 13, /* parameter not a GC */
BadIDChoice = 14, /* choice not in range or already used */
BadName = 15, /* font or color name doesn't exist */
BadLength = 16, /* Request length incorrect */
BadImplementation = 17, /* server is defective */
FirstExtensionError = 128,
LastExtensionError = 255,
}
[Flags]
public enum XEventMask : int
{
NoEventMask = 0,
KeyPressMask = (1 << 0),
KeyReleaseMask = (1 << 1),
ButtonPressMask = (1 << 2),
ButtonReleaseMask = (1 << 3),
EnterWindowMask = (1 << 4),
LeaveWindowMask = (1 << 5),
PointerMotionMask = (1 << 6),
PointerMotionHintMask = (1 << 7),
Button1MotionMask = (1 << 8),
Button2MotionMask = (1 << 9),
Button3MotionMask = (1 << 10),
Button4MotionMask = (1 << 11),
Button5MotionMask = (1 << 12),
ButtonMotionMask = (1 << 13),
KeymapStateMask = (1 << 14),
ExposureMask = (1 << 15),
VisibilityChangeMask = (1 << 16),
StructureNotifyMask = (1 << 17),
ResizeRedirectMask = (1 << 18),
SubstructureNotifyMask = (1 << 19),
SubstructureRedirectMask = (1 << 20),
FocusChangeMask = (1 << 21),
PropertyChangeMask = (1 << 22),
ColormapChangeMask = (1 << 23),
OwnerGrabButtonMask = (1 << 24)
}
[Flags]
public enum XModifierMask
{
ShiftMask = (1 << 0),
LockMask = (1 << 1),
ControlMask = (1 << 2),
Mod1Mask = (1 << 3),
Mod2Mask = (1 << 4),
Mod3Mask = (1 << 5),
Mod4Mask = (1 << 6),
Mod5Mask = (1 << 7),
Button1Mask = (1 << 8),
Button2Mask = (1 << 9),
Button3Mask = (1 << 10),
Button4Mask = (1 << 11),
Button5Mask = (1 << 12),
AnyModifier = (1 << 15)
}
[Flags]
public enum XCreateWindowFlags
{
CWBackPixmap = (1 << 0),
CWBackPixel = (1 << 1),
CWBorderPixmap = (1 << 2),
CWBorderPixel = (1 << 3),
CWBitGravity = (1 << 4),
CWWinGravity = (1 << 5),
CWBackingStore = (1 << 6),
CWBackingPlanes = (1 << 7),
CWBackingPixel = (1 << 8),
CWOverrideRedirect = (1 << 9),
CWSaveUnder = (1 << 10),
CWEventMask = (1 << 11),
CWDontPropagate = (1 << 12),
CWColormap = (1 << 13),
CWCursor = (1 << 14),
}
}

115
CPF.Linux/X11Info.cs Normal file
View File

@ -0,0 +1,115 @@
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using static CPF.Linux.XLib;
namespace CPF.Linux
{
public unsafe class X11Info
{
public IntPtr Display { get; }
public IntPtr DeferredDisplay { get; }
public int DefaultScreen { get; }
public IntPtr BlackPixel { get; }
public IntPtr RootWindow { get; }
public IntPtr DefaultRootWindow { get; }
public IntPtr DefaultCursor { get; }
public X11Atoms Atoms { get; }
public IntPtr Xim { get; }
public IntPtr DefaultColormap { get; }
public int RandrEventBase { get; }
public int RandrErrorBase { get; }
public Version RandrVersion { get; }
public int XInputOpcode { get; }
public int XInputEventBase { get; }
public int XInputErrorBase { get; }
public Version XInputVersion { get; }
//public IntPtr LastActivityTimestamp { get; set; }
public XVisualInfo TransparentVisualInfo { get; set; }
public unsafe X11Info(IntPtr display, IntPtr deferredDisplay)
{
Console.WriteLine(setlocale(6, ""));
////TODO: Open an actual XIM once we get support for preedit in our textbox
if (!XSupportsLocale())
{
Console.Error.WriteLine("X does not support your locale");
//return;
}
var locale = XSetLocaleModifiers("");
//if (string.IsNullOrWhiteSpace(locale))
//{
// Console.Error.WriteLine("Could not set X locale modifiers");
// //return;
//}
//Console.WriteLine(locale);
//Xim = LinuxPlatform.OpenIM(display);
Xim = XOpenIM(display, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
Console.WriteLine("xim:" + Xim);
//XGetIMValues(Xim, "queryIMValuesList", out var value, IntPtr.Zero);
//var list = Marshal.PtrToStructure<XIMValuesList>(value);
//var str = "";
//for (int i = 0; i < list.count_values; i++)
//{
// var first = Marshal.ReadIntPtr(list.supported_values + i * IntPtr.Size);
// str += Marshal.PtrToStringAnsi(first) + ",";
//}
//Console.WriteLine("queryIMValuesList:" + list.count_values + " " + str);
Display = display;
DeferredDisplay = deferredDisplay;
DefaultScreen = XDefaultScreen(display);
BlackPixel = XBlackPixel(display, DefaultScreen);
RootWindow = XRootWindow(display, DefaultScreen);
DefaultCursor = XCreateFontCursor(display, CursorFontShape.XC_top_left_arrow);
DefaultRootWindow = XDefaultRootWindow(display);
DefaultColormap = XDefaultColormap(display, DefaultScreen);
Atoms = new X11Atoms(display, DefaultScreen);
XMatchVisualInfo(Display, DefaultScreen, 32, 4, out var visual);
TransparentVisualInfo = visual;
Console.WriteLine("depth:" + visual.depth);
try
{
if (XRRQueryExtension(display, out int randrEventBase, out var randrErrorBase) != 0)
{
RandrEventBase = randrEventBase;
RandrErrorBase = randrErrorBase;
if (XRRQueryVersion(display, out var major, out var minor) != 0)
RandrVersion = new Version(major, minor);
}
}
catch
{
//Ignore, randr is not supported
}
try
{
if (XQueryExtension(display, "XInputExtension",
out var xiopcode, out var xievent, out var xierror))
{
int major = 2, minor = 2;
if (XIQueryVersion(display, ref major, ref minor) == Status.Success)
{
XInputVersion = new Version(major, minor);
XInputOpcode = xiopcode;
XInputEventBase = xievent;
XInputErrorBase = xierror;
}
}
}
catch
{
//Ignore, XI is not supported
}
}
}
}

2352
CPF.Linux/X11Key.cs Normal file

File diff suppressed because it is too large Load Diff

1999
CPF.Linux/X11Structs.cs Normal file

File diff suppressed because it is too large Load Diff

1800
CPF.Linux/X11Window.cs Normal file

File diff suppressed because it is too large Load Diff

35
CPF.Linux/XError.cs Normal file
View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CPF.Linux
{
static class XError
{
private static readonly XErrorHandler s_errorHandlerDelegate = Handler;
public static XErrorEvent LastError;
static int Handler(IntPtr display, ref XErrorEvent error)
{
LastError = error;
//StringBuilder stringBuilder = new StringBuilder(100);
//XLib.XGetErrorText(error.display, error.error_code, stringBuilder, stringBuilder.Length);
//Console.WriteLine("异常:" + stringBuilder.ToString() + " " + error.request_code + ":" + error.error_code);
return 0;
}
public static void ThrowLastError(string desc)
{
var err = LastError;
LastError = new XErrorEvent();
if (err.error_code == 0)
throw new Exception(desc);
throw new Exception(desc + ": " + err.error_code);
}
public static void Init()
{
XLib.XSetErrorHandler(s_errorHandlerDelegate);
}
}
}

357
CPF.Linux/XI2Manager.cs Normal file
View File

@ -0,0 +1,357 @@
using CPF.Drawing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CPF.Input;
using CPF.Controls;
using static CPF.Linux.XLib;
namespace CPF.Linux
{
public unsafe class XI2Manager
{
private static readonly XiEventType[] DefaultEventTypes = new XiEventType[]
{
XiEventType.XI_Motion,
XiEventType.XI_ButtonPress,
XiEventType.XI_ButtonRelease
};
private static readonly XiEventType[] MultiTouchEventTypes = new XiEventType[]
{
XiEventType.XI_TouchBegin,
XiEventType.XI_TouchUpdate,
XiEventType.XI_TouchEnd
};
private X11Info _x11;
private bool _multitouch;
//private Dictionary<IntPtr, IXI2Client> _clients = new Dictionary<IntPtr, IXI2Client>();
class DeviceInfo
{
public int Id { get; }
public XIValuatorClassInfo[] Valuators { get; private set; }
public XIScrollClassInfo[] Scrollers { get; private set; }
public DeviceInfo(XIDeviceInfo info)
{
Id = info.Deviceid;
Update(info.Classes, info.NumClasses);
}
public virtual void Update(XIAnyClassInfo** classes, int num)
{
var valuators = new List<XIValuatorClassInfo>();
var scrollers = new List<XIScrollClassInfo>();
for (var c = 0; c < num; c++)
{
if (classes[c]->Type == XiDeviceClass.XIValuatorClass)
valuators.Add(*((XIValuatorClassInfo**)classes)[c]);
if (classes[c]->Type == XiDeviceClass.XIScrollClass)
scrollers.Add(*((XIScrollClassInfo**)classes)[c]);
}
Valuators = valuators.ToArray();
Scrollers = scrollers.ToArray();
}
public void UpdateValuators(Dictionary<int, double> valuators)
{
foreach (var v in valuators)
{
if (Valuators.Length > v.Key)
Valuators[v.Key].Value = v.Value;
}
}
}
class PointerDeviceInfo : DeviceInfo
{
public PointerDeviceInfo(XIDeviceInfo info) : base(info)
{
}
public bool HasScroll(ParsedDeviceEvent ev)
{
foreach (var val in ev.Valuators)
if (Scrollers.Any(s => s.Number == val.Key))
return true;
return false;
}
public bool HasMotion(ParsedDeviceEvent ev)
{
foreach (var val in ev.Valuators)
if (Scrollers.All(s => s.Number != val.Key))
return true;
return false;
}
}
private PointerDeviceInfo _pointerDevice;
private LinuxPlatform _platform;
public bool Init(LinuxPlatform platform)
{
_platform = platform;
_x11 = platform.Info;
_multitouch = true;
var devices = (XIDeviceInfo*)XIQueryDevice(_x11.Display,
(int)XiPredefinedDeviceId.XIAllMasterDevices, out int num);
for (var c = 0; c < num; c++)
{
if (devices[c].Use == XiDeviceType.XIMasterPointer)
{
_pointerDevice = new PointerDeviceInfo(devices[c]);
break;
}
}
if (_pointerDevice == null)
return false;
/*
int mask = 0;
XISetMask(ref mask, XiEventType.XI_DeviceChanged);
var emask = new XIEventMask
{
Mask = &mask,
Deviceid = _pointerDevice.Id,
MaskLen = XiEventMaskLen
};
if (XISelectEvents(_x11.Display, _x11.RootWindow, &emask, 1) != Status.Success)
return false;
return true;
*/
return XiSelectEvents(_x11.Display, _x11.RootWindow, new Dictionary<int, List<XiEventType>>
{
[_pointerDevice.Id] = new List<XiEventType>
{
XiEventType.XI_DeviceChanged
}
}) == Status.Success;
}
public void AddWindow(IntPtr handle)
{
//_clients[xid] = window;
//System.Threading.Thread.Sleep(5000);
var eventsLength = DefaultEventTypes.Length;
if (_multitouch)
eventsLength += MultiTouchEventTypes.Length;
var events = new List<XiEventType>(eventsLength);
events.AddRange(DefaultEventTypes);
if (_multitouch)
events.AddRange(MultiTouchEventTypes);
var s = XiSelectEvents(_x11.Display, handle, new Dictionary<int, List<XiEventType>> { [_pointerDevice.Id] = events });
//Console.WriteLine(s);
//// We are taking over mouse input handling from here
//return XEventMask.PointerMotionMask
// | XEventMask.ButtonMotionMask
// | XEventMask.Button1MotionMask
// | XEventMask.Button2MotionMask
// | XEventMask.Button3MotionMask
// | XEventMask.Button4MotionMask
// | XEventMask.Button5MotionMask
// | XEventMask.ButtonPressMask
// | XEventMask.ButtonReleaseMask;
}
//public void OnWindowDestroyed(IntPtr xid) => _clients.Remove(xid);
public void OnEvent(XIEvent* xev, X11Window client)
{
if (xev->evtype == XiEventType.XI_DeviceChanged)
{
var changed = (XIDeviceChangedEvent*)xev;
_pointerDevice.Update(changed->Classes, changed->NumClasses);
}
if ((xev->evtype >= XiEventType.XI_ButtonPress && xev->evtype <= XiEventType.XI_Motion)
|| (xev->evtype >= XiEventType.XI_TouchBegin && xev->evtype <= XiEventType.XI_TouchEnd))
{
var dev = (XIDeviceEvent*)xev;
//if (_clients.TryGetValue(dev->EventWindow, out var client))
OnDeviceEvent(client, new ParsedDeviceEvent(dev));
}
}
void OnDeviceEvent(X11Window client, ParsedDeviceEvent ev)
{
//Console.WriteLine(ev.Type + " Emulated:" + ev.Emulated);
if (ev.Type == XiEventType.XI_TouchBegin
|| ev.Type == XiEventType.XI_TouchUpdate
|| ev.Type == XiEventType.XI_TouchEnd)
{
var type = ev.Type == XiEventType.XI_TouchBegin ?
EventType.TouchDown :
(ev.Type == XiEventType.XI_TouchUpdate ?
EventType.TouchMove :
EventType.TouchUp);
client.root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint { Id = ev.Detail, Position = ev.Position / client.RenderScaling }, client.root.InputManager.TouchDevice, client.root), client.root.LayoutManager.VisibleUIElements, type);
//client.ScheduleInput(new RawTouchEventArgs(client.TouchDevice, ev.Timestamp, client.InputRoot, type, ev.Position, ev.Modifiers, ev.Detail));
return;
}
//if (_multitouch && ev.Emulated)
// return;
if (ev.Type == XiEventType.XI_Motion)
{
Vector scrollDelta = default;
foreach (var v in ev.Valuators)
{
foreach (var scroller in _pointerDevice.Scrollers)
{
if (scroller.Number == v.Key)
{
var old = _pointerDevice.Valuators[scroller.Number].Value;
// Value was zero after reset, ignore the event and use it as a reference next time
if (old == 0)
continue;
var diff = (old - v.Value) / scroller.Increment;
if (scroller.ScrollType == XiScrollType.Horizontal)
scrollDelta = scrollDelta.WithX(scrollDelta.X + (float)diff);
else
scrollDelta = scrollDelta.WithY(scrollDelta.Y + (float)diff);
}
}
}
if (scrollDelta != default)
{ //client.ScheduleInput(new MouseWheelEventArgs(client.MouseDevice, ev.Timestamp, client.InputRoot, ev.Position, scrollDelta, ev.Modifiers));
//client.root.InputManager.MouseDevice.ProcessEvent(new MouseWheelEventArgs(client.root, ev.Modifiers.HasFlag(InputModifiers.LeftMouseButton), ev.Modifiers.HasFlag(InputModifiers.RightMouseButton), ev.Modifiers.HasFlag(InputModifiers.MiddleMouseButton), ev.Position / client.LayoutScaling, client.root.InputManager.MouseDevice, scrollDelta), client.root.LayoutManager.VisibleUIElements, EventType.MouseWheel);
client.MouseEvent(EventType.MouseWheel, ev.Position, ev.Modifiers, scrollDelta, MouseButton.None);
}
if (_pointerDevice.HasMotion(ev))
{
//client.ScheduleInput(new MouseEventArgs(client.MouseDevice, ev.Timestamp, client.InputRoot, RawPointerEventType.Move, ev.Position, ev.Modifiers));
//client.root.InputManager.MouseDevice.ProcessEvent(new MouseEventArgs(client.root, ev.Modifiers.HasFlag(InputModifiers.LeftMouseButton), ev.Modifiers.HasFlag(InputModifiers.RightMouseButton), ev.Modifiers.HasFlag(InputModifiers.MiddleMouseButton), ev.Position / client.LayoutScaling, client.root.InputManager.MouseDevice, ev.Emulated), client.root.LayoutManager.VisibleUIElements, EventType.MouseMove);
client.MouseEvent(EventType.MouseMove, ev.Position, ev.Modifiers, new Vector(), MouseButton.None, ev.Emulated);
}
}
if (ev.Type == XiEventType.XI_ButtonPress || ev.Type == XiEventType.XI_ButtonRelease)
{
if (!client.ActivateTransientChildIfNeeded())
{
var down = ev.Type == XiEventType.XI_ButtonPress;
//var type = ev.Button switch
//{
// 1 => down ? RawPointerEventType.LeftButtonDown : RawPointerEventType.LeftButtonUp,
// 2 => down ? RawPointerEventType.MiddleButtonDown : RawPointerEventType.MiddleButtonUp,
// 3 => down ? RawPointerEventType.RightButtonDown : RawPointerEventType.RightButtonUp,
// 8 => down ? RawPointerEventType.XButton1Down : RawPointerEventType.XButton1Up,
// 9 => down ? RawPointerEventType.XButton2Down : RawPointerEventType.XButton2Up,
// _ => (RawPointerEventType?)null
//};
//if (type.HasValue)
// client.ScheduleInput(new RawPointerEventArgs(client.MouseDevice, ev.Timestamp, client.InputRoot,
// type.Value, ev.Position, ev.Modifiers));
MouseButton? mouseButton = null;
switch (ev.Button)
{
case 1:
mouseButton = MouseButton.Left;
break;
case 2:
mouseButton = MouseButton.Middle;
break;
case 3:
mouseButton = MouseButton.Right;
break;
case 8:
mouseButton = MouseButton.XButton1;
break;
case 9:
mouseButton = MouseButton.XButton2;
break;
}
if (mouseButton != null)
{
//client.root.InputManager.MouseDevice.ProcessEvent(new MouseButtonEventArgs(client.root, ev.Modifiers.HasFlag(InputModifiers.LeftMouseButton), ev.Modifiers.HasFlag(InputModifiers.RightMouseButton), ev.Modifiers.HasFlag(InputModifiers.MiddleMouseButton), ev.Position / client.LayoutScaling, client.root.InputManager.MouseDevice, mouseButton.Value, ev.Emulated), client.root.LayoutManager.VisibleUIElements, down ? EventType.MouseDown : EventType.MouseUp);
client.MouseEvent(down ? EventType.MouseDown : EventType.MouseUp, ev.Position, ev.Modifiers, new Vector(), mouseButton.Value, ev.Emulated);
}
}
}
_pointerDevice.UpdateValuators(ev.Valuators);
}
}
unsafe class ParsedDeviceEvent
{
public XiEventType Type { get; }
public InputModifiers Modifiers { get; }
public ulong Timestamp { get; }
public Point Position { get; }
public int Button { get; set; }
public int Detail { get; set; }
public bool Emulated { get; set; }
public Dictionary<int, double> Valuators { get; }
public ParsedDeviceEvent(XIDeviceEvent* ev)
{
Type = ev->evtype;
Timestamp = (ulong)ev->time.ToInt64();
var state = (XModifierMask)ev->mods.Effective;
if (state.HasFlag(XModifierMask.ShiftMask))
Modifiers |= InputModifiers.Shift;
if (state.HasFlag(XModifierMask.ControlMask))
Modifiers |= InputModifiers.Control;
if (state.HasFlag(XModifierMask.Mod1Mask))
Modifiers |= InputModifiers.Alt;
//if (state.HasFlag(XModifierMask.Mod4Mask))
// Modifiers |= InputModifiers.Meta;
if (ev->buttons.MaskLen > 0)
{
var buttons = ev->buttons.Mask;
if (XIMaskIsSet(buttons, 1))
Modifiers |= InputModifiers.LeftMouseButton;
if (XIMaskIsSet(buttons, 2))
Modifiers |= InputModifiers.MiddleMouseButton;
if (XIMaskIsSet(buttons, 3))
Modifiers |= InputModifiers.RightMouseButton;
//if (XIMaskIsSet(buttons, 8))
// Modifiers |= InputModifiers.XButton1MouseButton;
//if (XIMaskIsSet(buttons, 9))
// Modifiers |= InputModifiers.XButton2MouseButton;
}
Valuators = new Dictionary<int, double>();
Position = new Point((float)ev->event_x, (float)ev->event_y);
var values = ev->valuators.Values;
for (var c = 0; c < ev->valuators.MaskLen * 8; c++)
if (XIMaskIsSet(ev->valuators.Mask, c))
Valuators[c] = *values++;
if (Type == XiEventType.XI_ButtonPress || Type == XiEventType.XI_ButtonRelease)
Button = ev->detail;
Detail = ev->detail;
Emulated = ev->flags.HasFlag(XiDeviceEventFlags.XIPointerEmulated);
}
}
//interface IXI2Client
//{
// View InputRoot { get; }
// void ScheduleInput(InputEventArgs args);
// MouseDevice MouseDevice { get; }
// TouchDevice TouchDevice { get; }
//}
}

303
CPF.Linux/XIStructs.cs Normal file
View File

@ -0,0 +1,303 @@
using System;
using System.Runtime.InteropServices;
using Bool = System.Boolean;
using Atom = System.IntPtr;
namespace CPF.Linux
{
[StructLayout(LayoutKind.Sequential)]
public struct XScreenSaverInfo
{
public IntPtr window;
public int state;
public int kind;
public ulong til_or_since;
public ulong idle;
public ulong eventMask;
}
[StructLayout(LayoutKind.Sequential)]
public struct XIAddMasterInfo
{
public int Type;
public IntPtr Name;
public Bool SendCore;
public Bool Enable;
}
[StructLayout(LayoutKind.Sequential)]
public struct XIRemoveMasterInfo
{
public int Type;
public int Deviceid;
public int ReturnMode; /* AttachToMaster, Floating */
public int ReturnPointer;
public int ReturnKeyboard;
};
[StructLayout(LayoutKind.Sequential)]
public struct XIAttachSlaveInfo
{
public int Type;
public int Deviceid;
public int NewMaster;
};
[StructLayout(LayoutKind.Sequential)]
public struct XIDetachSlaveInfo
{
public int Type;
public int Deviceid;
};
[StructLayout(LayoutKind.Explicit)]
struct XIAnyHierarchyChangeInfo
{
[FieldOffset(0)]
public int type; /* must be first element */
[FieldOffset(4)]
public XIAddMasterInfo add;
[FieldOffset(4)]
public XIRemoveMasterInfo remove;
[FieldOffset(4)]
public XIAttachSlaveInfo attach;
[FieldOffset(4)]
public XIDetachSlaveInfo detach;
};
[StructLayout(LayoutKind.Sequential)]
public struct XIModifierState
{
public int Base;
public int Latched;
public int Locked;
public int Effective;
};
[StructLayout(LayoutKind.Sequential)]
public unsafe struct XIButtonState
{
public int MaskLen;
public byte* Mask;
};
[StructLayout(LayoutKind.Sequential)]
public unsafe struct XIValuatorState
{
public int MaskLen;
public byte* Mask;
public double* Values;
};
[StructLayout(LayoutKind.Sequential)]
public unsafe struct XIEventMask
{
public int Deviceid;
public int MaskLen;
public int* Mask;
};
[StructLayout(LayoutKind.Sequential)]
public struct XIAnyClassInfo
{
public XiDeviceClass Type;
public int Sourceid;
};
[StructLayout(LayoutKind.Sequential)]
unsafe struct XIButtonClassInfo
{
public int Type;
public int Sourceid;
public int NumButtons;
public IntPtr* Labels;
public XIButtonState State;
};
[StructLayout(LayoutKind.Sequential)]
unsafe struct XIKeyClassInfo
{
public int Type;
public int Sourceid;
public int NumKeycodes;
public int* Keycodes;
};
[StructLayout(LayoutKind.Sequential)]
struct XIValuatorClassInfo
{
public int Type;
public int Sourceid;
public int Number;
public IntPtr Label;
public double Min;
public double Max;
public double Value;
public int Resolution;
public int Mode;
};
/* new in XI 2.1 */
[StructLayout(LayoutKind.Sequential)]
struct XIScrollClassInfo
{
public int Type;
public int Sourceid;
public int Number;
public XiScrollType ScrollType;
public double Increment;
public int Flags;
};
enum XiScrollType
{
Vertical = 1,
Horizontal = 2
}
[StructLayout(LayoutKind.Sequential)]
struct XITouchClassInfo
{
public int Type;
public int Sourceid;
public int Mode;
public int NumTouches;
};
[StructLayout(LayoutKind.Sequential)]
public unsafe struct XIDeviceInfo
{
public int Deviceid;
public IntPtr Name;
public XiDeviceType Use;
public int Attachment;
public Bool Enabled;
public int NumClasses;
public XIAnyClassInfo** Classes;
}
public enum XiDeviceType
{
XIMasterPointer = 1,
XIMasterKeyboard = 2,
XISlavePointer = 3,
XISlaveKeyboard = 4,
XIFloatingSlave = 5
}
enum XiPredefinedDeviceId : int
{
XIAllDevices = 0,
XIAllMasterDevices = 1
}
public enum XiDeviceClass
{
XIKeyClass = 0,
XIButtonClass = 1,
XIValuatorClass = 2,
XIScrollClass = 3,
XITouchClass = 8,
}
[StructLayout(LayoutKind.Sequential)]
unsafe struct XIDeviceChangedEvent
{
public int Type; /* GenericEvent */
public ulong Serial; /* # of last request processed by server */
public Bool SendEvent; /* true if this came from a SendEvent request */
public IntPtr Display; /* Display the event was read from */
public int Extension; /* XI extension offset */
public int Evtype; /* XI_DeviceChanged */
public IntPtr Time;
public int Deviceid; /* id of the device that changed */
public int Sourceid; /* Source for the new classes. */
public int Reason; /* Reason for the change */
public int NumClasses;
public XIAnyClassInfo** Classes; /* same as in XIDeviceInfo */
}
[StructLayout(LayoutKind.Sequential)]
struct XIDeviceEvent
{
public XEventName type; /* GenericEvent */
public ulong serial; /* # of last request processed by server */
public Bool send_event; /* true if this came from a SendEvent request */
public IntPtr display; /* Display the event was read from */
public int extension; /* XI extension offset */
public XiEventType evtype;
public IntPtr time;
public int deviceid;
public int sourceid;
public int detail;
public IntPtr RootWindow;
public IntPtr EventWindow;
public IntPtr ChildWindow;
public double root_x;
public double root_y;
public double event_x;
public double event_y;
public XiDeviceEventFlags flags;
public XIButtonState buttons;
public XIValuatorState valuators;
public XIModifierState mods;
public XIModifierState group;
}
[Flags]
public enum XiDeviceEventFlags : int
{
None = 0,
XIPointerEmulated = (1 << 16)
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct XIEvent
{
public int type; /* GenericEvent */
public ulong serial; /* # of last request processed by server */
public Bool send_event; /* true if this came from a SendEvent request */
public IntPtr display; /* Display the event was read from */
public int extension; /* XI extension offset */
public XiEventType evtype;
public IntPtr time;
}
[StructLayout(LayoutKind.Sequential)]
public struct XRectangle
{
public short x, y;
public ushort width, height;
}
public enum XiEventType
{
XI_DeviceChanged = 1,
XI_KeyPress = 2,
XI_KeyRelease = 3,
XI_ButtonPress = 4,
XI_ButtonRelease = 5,
XI_Motion = 6,
XI_Enter = 7,
XI_Leave = 8,
XI_FocusIn = 9,
XI_FocusOut = 10,
XI_HierarchyChanged = 11,
XI_PropertyEvent = 12,
XI_RawKeyPress = 13,
XI_RawKeyRelease = 14,
XI_RawButtonPress = 15,
XI_RawButtonRelease = 16,
XI_RawMotion = 17,
XI_TouchBegin = 18 /* XI 2.2 */,
XI_TouchUpdate = 19,
XI_TouchEnd = 20,
XI_TouchOwnership = 21,
XI_RawTouchBegin = 22,
XI_RawTouchUpdate = 23,
XI_RawTouchEnd = 24,
XI_BarrierHit = 25 /* XI 2.3 */,
XI_BarrierLeave = 26,
XI_LASTEVENT = XI_BarrierLeave,
}
}

926
CPF.Linux/XLib.cs Normal file
View File

@ -0,0 +1,926 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace CPF.Linux
{
public unsafe static class XLib
{
const string libX11 = "libX11.so.6";
const string libX11Randr = "libXrandr.so.2";
const string libX11Ext = "libXext.so.6";
const string libXInput = "libXi.so.6";
[DllImport(libX11)]
public static extern IntPtr XOpenDisplay(IntPtr display);
[DllImport(libX11)]
public static extern int XCloseDisplay(IntPtr display);
[DllImport(libX11)]
public static extern IntPtr XSynchronize(IntPtr display, bool onoff);
[DllImport(libX11)]
public static extern IntPtr XCreateWindow(IntPtr display, IntPtr parent, int x, int y, int width, int height,
int border_width, int depth, int xclass, IntPtr visual, UIntPtr valuemask,
ref XSetWindowAttributes attributes);
[DllImport(libX11)]
public static extern IntPtr XCreateWindow(IntPtr display, IntPtr parent, int x, int y, uint width,
uint height, uint border_width, int depth, uint @class, IntPtr visual, ulong valuemask,
ref XSetWindowAttributes attributes);
[DllImport(libX11)]
public static extern Status XChangeWindowAttributes(IntPtr display, IntPtr window, ulong valuemask, ref XSetWindowAttributes attributes);
[DllImport(libX11)]
public static extern IntPtr XCreateSimpleWindow(IntPtr display, IntPtr parent, int x, int y, int width,
int height, int border_width, IntPtr border, IntPtr background);
[DllImport(libX11)]
public static extern int XMapWindow(IntPtr display, IntPtr window);
[DllImport(libX11)]
public static extern int XUnmapWindow(IntPtr display, IntPtr window);
[DllImport(libX11)]
public static extern int XMapSubindows(IntPtr display, IntPtr window);
[DllImport(libX11)]
public static extern int XUnmapSubwindows(IntPtr display, IntPtr window);
[DllImport(libX11)]
public static extern IntPtr XRootWindow(IntPtr display, int screen_number);
[DllImport(libX11)]
public static extern IntPtr XDefaultRootWindow(IntPtr display);
[DllImport(libX11)]
public static extern IntPtr XNextEvent(IntPtr display, out XEvent xevent);
[DllImport(libX11)]
public static extern int XConnectionNumber(IntPtr diplay);
[DllImport(libX11)]
public static extern int XPending(IntPtr diplay);
[DllImport(libX11)]
public static extern IntPtr XSelectInput(IntPtr display, IntPtr window, IntPtr mask);
[DllImport(libX11)]
public static extern int XDestroyWindow(IntPtr display, IntPtr window);
[DllImport(libX11)]
public static extern int XReparentWindow(IntPtr display, IntPtr window, IntPtr parent, int x, int y);
[DllImport(libX11)]
public static extern void XMoveWindow(IntPtr display, IntPtr window, int x, int y);
[DllImport(libX11)]
public static extern int XMoveResizeWindow(IntPtr display, IntPtr window, int x, int y, int width, int height);
[DllImport(libX11)]
public static extern int XResizeWindow(IntPtr display, IntPtr window, int width, int height);
[DllImport(libX11)]
public static extern Status XGetWindowAttributes(IntPtr display, IntPtr window, ref XWindowAttributes attributes);
[DllImport(libX11)]
public static extern int XFlush(IntPtr display);
[DllImport(libX11)]
public static extern int XSetWMName(IntPtr display, IntPtr window, ref XTextProperty text_prop);
[DllImport(libX11)]
public static extern void XSetWMIconName(IntPtr display, IntPtr window, ref XTextProperty text_prop);
[DllImport(libX11)]
public static extern int XStoreName(IntPtr display, IntPtr window, string window_name);
[DllImport(libX11)]
public static extern int XSetIconName(IntPtr display, IntPtr window, string window_name);
[DllImport(libX11)]
public static extern int XFetchName(IntPtr display, IntPtr window, ref IntPtr window_name);
[DllImport(libX11)]
public static extern Status XSendEvent(IntPtr display, IntPtr window, bool propagate, IntPtr event_mask,
ref XEvent send_event);
[DllImport(libX11)]
public static extern int XQueryTree(IntPtr display, IntPtr window, out IntPtr root_return,
out IntPtr parent_return, out IntPtr children_return, out int nchildren_return);
[DllImport(libX11)]
public static extern int XFree(IntPtr data);
[DllImport(libX11)]
public static extern int XRaiseWindow(IntPtr display, IntPtr window);
[DllImport(libX11)]
public static extern uint XLowerWindow(IntPtr display, IntPtr window);
[DllImport(libX11)]
public static extern uint XConfigureWindow(IntPtr display, IntPtr window, ChangeWindowFlags value_mask,
ref XWindowChanges values);
public static uint XConfigureResizeWindow(IntPtr display, IntPtr window, PixelSize size)
=> XConfigureResizeWindow(display, window, size.Width, size.Height);
public static uint XConfigureResizeWindow(IntPtr display, IntPtr window, int width, int height)
{
var changes = new XWindowChanges
{
width = width,
height = height
};
return XConfigureWindow(display, window, ChangeWindowFlags.CWHeight | ChangeWindowFlags.CWWidth,
ref changes);
}
[DllImport(libX11)]
public static extern IntPtr XInternAtom(IntPtr display, string atom_name, bool only_if_exists);
[DllImport(libX11)]
public static extern int XInternAtoms(IntPtr display, string[] atom_names, int atom_count, bool only_if_exists,
IntPtr[] atoms);
[DllImport(libX11)]
public static extern IntPtr XGetAtomName(IntPtr display, IntPtr atom);
public static string GetAtomName(IntPtr display, IntPtr atom)
{
var ptr = XGetAtomName(display, atom);
if (ptr == IntPtr.Zero)
return null;
var s = Marshal.PtrToStringAnsi(ptr);
XFree(ptr);
return s;
}
[DllImport(libX11)]
public static extern int XSetWMProtocols(IntPtr display, IntPtr window, IntPtr[] protocols, int count);
[DllImport(libX11)]
public static extern int XGrabPointer(IntPtr display, IntPtr window, bool owner_events, EventMask event_mask,
GrabMode pointer_mode, GrabMode keyboard_mode, IntPtr confine_to, IntPtr cursor, IntPtr timestamp);
[DllImport(libX11)]
public static extern int XUngrabPointer(IntPtr display, IntPtr timestamp);
[DllImport(libX11)]
public static extern bool XQueryPointer(IntPtr display, IntPtr window, out IntPtr root, out IntPtr child,
out int root_x, out int root_y, out int win_x, out int win_y, out int keys_buttons);
[DllImport(libX11)]
public static extern bool XTranslateCoordinates(IntPtr display, IntPtr src_w, IntPtr dest_w, int src_x,
int src_y, out int intdest_x_return, out int dest_y_return, out IntPtr child_return);
[DllImport(libX11)]
public static extern bool XGetGeometry(IntPtr display, IntPtr window, out IntPtr root, out int x, out int y,
out int width, out int height, out int border_width, out int depth);
[DllImport(libX11)]
public static extern bool XGetGeometry(IntPtr display, IntPtr window, IntPtr root, out int x, out int y,
out int width, out int height, IntPtr border_width, IntPtr depth);
[DllImport(libX11)]
public static extern bool XGetGeometry(IntPtr display, IntPtr window, IntPtr root, out int x, out int y,
IntPtr width, IntPtr height, IntPtr border_width, IntPtr depth);
[DllImport(libX11)]
public static extern bool XGetGeometry(IntPtr display, IntPtr window, IntPtr root, IntPtr x, IntPtr y,
out int width, out int height, IntPtr border_width, IntPtr depth);
[DllImport(libX11)]
public static extern uint XWarpPointer(IntPtr display, IntPtr src_w, IntPtr dest_w, int src_x, int src_y,
uint src_width, uint src_height, int dest_x, int dest_y);
[DllImport(libX11)]
public static extern int XClearWindow(IntPtr display, IntPtr window);
[DllImport(libX11)]
public static extern int XClearArea(IntPtr display, IntPtr window, int x, int y, int width, int height,
bool exposures);
// Colormaps
[DllImport(libX11)]
public static extern IntPtr XDefaultScreenOfDisplay(IntPtr display);
[DllImport(libX11)]
public static extern int XScreenNumberOfScreen(IntPtr display, IntPtr Screen);
[DllImport(libX11)]
public static extern IntPtr XDefaultVisual(IntPtr display, int screen_number);
[DllImport(libX11)]
public static extern uint XDefaultDepth(IntPtr display, int screen_number);
[DllImport(libX11)]
public static extern int XDefaultScreen(IntPtr display);
[DllImport(libX11)]
public static extern IntPtr XDefaultColormap(IntPtr display, int screen_number);
[DllImport(libX11)]
public static extern int XLookupColor(IntPtr display, IntPtr Colormap, string Coloranem,
ref XColor exact_def_color, ref XColor screen_def_color);
[DllImport(libX11)]
public static extern int XAllocColor(IntPtr display, IntPtr Colormap, ref XColor colorcell_def);
[DllImport(libX11)]
public static extern int XSetTransientForHint(IntPtr display, IntPtr window, IntPtr parent);
[DllImport(libX11)]
public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type,
int format, PropertyMode mode, ref MotifWmHints data, int nelements);
[DllImport(libX11)]
public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type,
int format, PropertyMode mode, ref uint value, int nelements);
[DllImport(libX11)]
public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type,
int format, PropertyMode mode, ref IntPtr value, int nelements);
[DllImport(libX11)]
public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type,
int format, PropertyMode mode, uint[] data, int nelements);
[DllImport(libX11)]
public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type,
int format, PropertyMode mode, int[] data, int nelements);
[DllImport(libX11)]
public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type,
int format, PropertyMode mode, IntPtr[] data, int nelements);
[DllImport(libX11)]
public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type,
int format, PropertyMode mode, void* data, int nelements);
[DllImport(libX11)]
public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type,
int format, PropertyMode mode, IntPtr atoms, int nelements);
[DllImport(libX11, CharSet = CharSet.Ansi)]
public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type,
int format, PropertyMode mode, string text, int text_length);
[DllImport(libX11)]
public static extern int XDeleteProperty(IntPtr display, IntPtr window, IntPtr property);
// Drawing
[DllImport(libX11)]
public static extern IntPtr XCreateGC(IntPtr display, IntPtr window, IntPtr valuemask, ref XGCValues values);
[DllImport(libX11)]
public static extern int XFreeGC(IntPtr display, IntPtr gc);
[DllImport(libX11)]
public static extern int XSetFunction(IntPtr display, IntPtr gc, GXFunction function);
[DllImport(libX11)]
internal static extern int XSetLineAttributes(IntPtr display, IntPtr gc, int line_width, GCLineStyle line_style,
GCCapStyle cap_style, GCJoinStyle join_style);
[DllImport(libX11)]
public static extern int XDrawLine(IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int x2, int y2);
[DllImport(libX11)]
public static extern int XDrawRectangle(IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int width,
int height);
[DllImport(libX11)]
public static extern int XFillRectangle(IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int width,
int height);
[DllImport(libX11)]
public static extern int XSetWindowBackground(IntPtr display, IntPtr window, IntPtr background);
[DllImport(libX11)]
public static extern int XCopyArea(IntPtr display, IntPtr src, IntPtr dest, IntPtr gc, int src_x, int src_y,
int width, int height, int dest_x, int dest_y);
[DllImport(libX11)]
public static extern int XGetWindowProperty(IntPtr display, IntPtr window, IntPtr atom, IntPtr long_offset,
IntPtr long_length, bool delete, IntPtr req_type, out IntPtr actual_type, out int actual_format,
out IntPtr nitems, out IntPtr bytes_after, out IntPtr prop);
[DllImport(libX11)]
public static extern int XSetInputFocus(IntPtr display, IntPtr window, RevertTo revert_to, IntPtr time);
[DllImport(libX11)]
public static extern int XIconifyWindow(IntPtr display, IntPtr window, int screen_number);
[DllImport(libX11)]
public static extern int XDefineCursor(IntPtr display, IntPtr window, IntPtr cursor);
[DllImport(libX11)]
public static extern int XUndefineCursor(IntPtr display, IntPtr window);
[DllImport(libX11)]
public static extern int XFreeCursor(IntPtr display, IntPtr cursor);
[DllImport(libX11)]
public static extern IntPtr XCreateFontCursor(IntPtr display, CursorFontShape shape);
[DllImport(libX11)]
public static extern IntPtr XCreatePixmapCursor(IntPtr display, IntPtr source, IntPtr mask,
ref XColor foreground_color, ref XColor background_color, int x_hot, int y_hot);
[DllImport(libX11)]
public static extern IntPtr XCreatePixmapFromBitmapData(IntPtr display, IntPtr drawable, byte[] data, int width,
int height, IntPtr fg, IntPtr bg, int depth);
[DllImport(libX11)]
public static extern IntPtr XCreatePixmap(IntPtr display, IntPtr d, int width, int height, int depth);
[DllImport(libX11)]
public static extern IntPtr XFreePixmap(IntPtr display, IntPtr pixmap);
[DllImport(libX11)]
public static extern int XQueryBestCursor(IntPtr display, IntPtr drawable, int width, int height,
out int best_width, out int best_height);
[DllImport(libX11)]
public static extern IntPtr XWhitePixel(IntPtr display, int screen_no);
[DllImport(libX11)]
public static extern IntPtr XBlackPixel(IntPtr display, int screen_no);
[DllImport(libX11)]
public static extern void XGrabServer(IntPtr display);
[DllImport(libX11)]
public static extern void XUngrabServer(IntPtr display);
[DllImport(libX11)]
public static extern void XGetWMNormalHints(IntPtr display, IntPtr window, ref XSizeHints hints,
out IntPtr supplied_return);
[DllImport(libX11)]
public static extern void XSetWMNormalHints(IntPtr display, IntPtr window, ref XSizeHints hints);
[DllImport(libX11)]
public static extern void XSetZoomHints(IntPtr display, IntPtr window, ref XSizeHints hints);
[DllImport(libX11)]
public static extern void XSetWMHints(IntPtr display, IntPtr window, ref XWMHints wmhints);
[DllImport(libX11)]
public static extern int XGetIconSizes(IntPtr display, IntPtr window, out IntPtr size_list, out int count);
[DllImport(libX11)]
public static extern IntPtr XSetErrorHandler(XErrorHandler error_handler);
[DllImport(libX11)]
public static extern IntPtr XGetErrorText(IntPtr display, byte code, StringBuilder buffer, int length);
[DllImport(libX11)]
public static extern int XInitThreads();
[DllImport(libX11)]
public static extern int XConvertSelection(IntPtr display, IntPtr selection, IntPtr target, IntPtr property,
IntPtr requestor, IntPtr time);
[DllImport(libX11)]
public static extern IntPtr XGetSelectionOwner(IntPtr display, IntPtr selection);
[DllImport(libX11)]
public static extern int XSetSelectionOwner(IntPtr display, IntPtr selection, IntPtr owner, IntPtr time);
[DllImport(libX11)]
public static extern int XSetPlaneMask(IntPtr display, IntPtr gc, IntPtr mask);
[DllImport(libX11)]
public static extern int XSetForeground(IntPtr display, IntPtr gc, IntPtr foreground);
[DllImport(libX11)]
public static extern int XSetBackground(IntPtr display, IntPtr gc, IntPtr background);
[DllImport(libX11)]
public static extern int XBell(IntPtr display, int percent);
[DllImport(libX11)]
public static extern int XChangeActivePointerGrab(IntPtr display, EventMask event_mask, IntPtr cursor,
IntPtr time);
[DllImport(libX11)]
public static extern bool XFilterEvent(ref XEvent xevent, IntPtr window);
[DllImport(libX11)]
public static extern void XkbSetDetectableAutoRepeat(IntPtr display, bool detectable, IntPtr supported);
[DllImport(libX11)]
public static extern void XPeekEvent(IntPtr display, out XEvent xevent);
[DllImport(libX11)]
public static extern void XMatchVisualInfo(IntPtr display, int screen, int depth, int klass, out XVisualInfo info);
[DllImport(libX11)]
public static extern IntPtr XLockDisplay(IntPtr display);
[DllImport(libX11)]
public static extern IntPtr XUnlockDisplay(IntPtr display);
[DllImport(libX11)]
public static extern IntPtr XCreateGC(IntPtr display, IntPtr drawable, ulong valuemask, IntPtr values);
[DllImport(libX11)]
public static extern int XInitImage(ref XImage image);
[DllImport(libX11)]
public static extern int XDestroyImage(ref XImage image);
[DllImport(libX11)]
public static extern int XDestroyImage(IntPtr image);
[DllImport(libX11)]
public static extern int XPutImage(IntPtr display, IntPtr drawable, IntPtr gc, ref XImage image,
int srcx, int srcy, int destx, int desty, uint width, uint height);
[DllImport(libX11)]
public static extern XImage* XGetSubImage(IntPtr display, IntPtr Drawable, int x, int y, uint width, uint height, int plane_mask, int format, XImage* dest_image, int dest_x, int dest_y);
[DllImport(libX11, EntryPoint = "XGetImage")]
public extern static IntPtr XGetImage(IntPtr display, IntPtr drawable, int src_x, int src_y, int width, int height, int pane, int format);
[DllImport(libX11, EntryPoint = "XGetPixel")]
public extern static int XGetPixel(IntPtr image, int x, int y);
[DllImport(libX11)]
public static extern int XSync(IntPtr display, bool discard);
[DllImport(libX11)]
public static extern IntPtr XCreateColormap(IntPtr display, IntPtr window, IntPtr visual, int create);
public enum XLookupStatus
{
XBufferOverflow = -1,
XLookupNone = 1,
XLookupChars = 2,
XLookupKeySym = 3,
XLookupBoth = 4
}
public class LibC
{
public static int LC_ALL = 6;
public static int LC_COLLATE = 3;
public static int LC_CTYPE = 0;
}
[DllImport("libc")]
public static extern string setlocale(int category, string locale);
[DllImport("libc")]
public static extern string setlocale(int category, IntPtr locale);
[DllImport("libc")]
public static extern double atof(IntPtr str);
[DllImport(libX11)]
public static extern IntPtr XCreateFontSet(IntPtr display, string base_font_name_list, out IntPtr missing_charset_list, out int missing_charset_count_return, ref IntPtr def_string);
//[DllImport(libX11)]
//public static extern IntPtr XCreateFontSet(IntPtr display, string base_font_name_list, out IntPtr missing_charset_list, out int missing_charset_count_return, out string def_string);
[DllImport(libX11)]
public static extern IntPtr XVaCreateNestedList(int zero, string xname, IntPtr fontSet, IntPtr z);
[DllImport(libX11)]
public static extern IntPtr XVaCreateNestedList(int zero, string xname, ref XRectangle rect, IntPtr z);
[DllImport(libX11, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr XVaCreateNestedList(int dummy, IntPtr name0, XPoint value0, IntPtr terminator);
[DllImport(libX11)]
public static extern IntPtr XVaCreateNestedList(int dummy, IntPtr name0, XPoint value0, IntPtr name1, IntPtr value1, IntPtr terminator);
[DllImport(libX11)]
public static extern IntPtr XVaCreateNestedList(int dummy, string name0, XPoint value0, string name1, IntPtr value1, IntPtr terminator);
[DllImport(libX11)]
internal extern static int XLookupString(ref XEvent xevent, byte[] buffer, int num_bytes, out IntPtr keysym, out IntPtr status);
[DllImport(libX11)]
internal extern static int Xutf8LookupString(IntPtr xic, ref XEvent xevent, byte[] buffer, int num_bytes, out IntPtr keysym, out XLookupStatus status);
//[DllImport(libX11)]
//public static extern unsafe int XLookupString(ref XEvent xevent, void* buffer, int num_bytes, out IntPtr keysym, out IntPtr status);
//[DllImport(libX11)]
//public static extern unsafe int Xutf8LookupString(IntPtr xic, ref XEvent xevent, void* buffer, int num_bytes, out IntPtr keysym, out IntPtr status);
[DllImport(libX11)]
public static extern unsafe int XwcLookupString(IntPtr xic, ref XEvent xevent, void* buffer, int num_bytes, out IntPtr keysym, out IntPtr status);
[DllImport(libX11)]
public static extern unsafe int XmbLookupString(IntPtr xic, ref XKeyEvent xevent, void* buffer, int num_bytes, out X11Key keysym, out LookupStatus status);
[DllImport(libX11)]
public static extern unsafe IntPtr XKeycodeToKeysym(IntPtr display, int keycode, int index);
//[DllImport(libX11)]
//public static extern string XSetLocaleModifiers(IntPtr modifiers);
[DllImport(libX11)]
public static extern IntPtr XSetLocaleModifiers(string mods);
[DllImport(libX11)]
public static extern IntPtr XOpenIM(IntPtr display, IntPtr rdb, IntPtr res_name, IntPtr res_class);
[DllImport(libX11)]
public static extern string XLocaleOfIM(IntPtr xim);
[DllImport(libX11)]
public static extern IntPtr XCreateIC(IntPtr xim, string name, XIMProperties im_style, string name2, IntPtr value2, IntPtr terminator);
[DllImport(libX11)]
public static extern IntPtr XCreateIC(IntPtr xim, string name, XIMProperties im_style, string name2, IntPtr value2, string name3, IntPtr value3, IntPtr terminator);
[DllImport(libX11)]
public static extern IntPtr XCreateIC(IntPtr xim, string name, XIMProperties im_style, string name2, IntPtr value2, string name3, IntPtr value3, string name4, IntPtr value4, IntPtr terminator);
[DllImport(libX11)]
public static extern void XSetICFocus(IntPtr xic);
[DllImport(libX11, CallingConvention = CallingConvention.Cdecl)]
public static extern void XSetICValues(IntPtr xic, string name, IntPtr value, IntPtr terminator);
[DllImport(libX11, CallingConvention = CallingConvention.Cdecl)]
public static extern string XSetICValues(IntPtr xic, __arglist);
[DllImport(libX11, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr XGetICValues(IntPtr xic, string p1, ref XIMStyles p2);
[DllImport(libX11, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr XGetICValues(IntPtr xic, string p1, ref long p2, IntPtr zero);
[DllImport(libX11, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr XGetICValues(IntPtr xic, string p1, IntPtr p2, IntPtr zero);
[DllImport(libX11)]
public static extern string XGetIMValues(IntPtr xim, string name, out IntPtr value, IntPtr terminator);
[DllImport(libX11)]
public static extern string Xutf8ResetIC(IntPtr xic);
[DllImport(libX11)]
public static extern void XUnsetICFocus(IntPtr xic);
[DllImport(libX11)]
public static extern bool XSupportsLocale();
[DllImport(libX11)]
public static extern void XCloseIM(IntPtr xim);
[DllImport(libX11)]
public static extern void XDestroyIC(IntPtr xic);
[DllImport(libX11)]
public static extern bool XQueryExtension(IntPtr display, [MarshalAs(UnmanagedType.LPStr)] string name,
out int majorOpcode, out int firstEvent, out int firstError);
[DllImport(libX11)]
public static extern bool XGetEventData(IntPtr display, void* cookie);
[DllImport(libX11)]
public static extern void XFreeEventData(IntPtr display, void* cookie);
[DllImport(libX11)]
public static extern IntPtr XResourceManagerString(IntPtr display);
[DllImport(libX11)]
public static extern void XrmInitialize();
[DllImport(libX11)]
public static extern IntPtr XrmGetStringDatabase(IntPtr data);
[DllImport(libX11)]
public static extern bool XrmGetResource(IntPtr db, string str_name, string str_class, ref IntPtr str_type_return, ref XrmValue value);
[DllImport("libXss.so.1")]
public static extern Status XScreenSaverQueryInfo(IntPtr display, IntPtr window, ref XScreenSaverInfo saver_info);
[DllImport(libX11Randr)]
public static extern IntPtr XRRGetScreenInfo(IntPtr dpy, IntPtr window);
[DllImport(libX11Randr)]
public static extern int XRRQueryExtension(IntPtr dpy,
out int event_base_return,
out int error_base_return);
[DllImport(libX11Randr)]
public static extern int XRRQueryVersion(IntPtr dpy,
out int major_version_return,
out int minor_version_return);
[DllImport(libX11Randr)]
public static extern XRRMonitorInfo* XRRGetMonitors(IntPtr dpy, IntPtr window, bool get_active, out int nmonitors);
[DllImport(libX11Randr)]
public static extern IntPtr* XRRListOutputProperties(IntPtr dpy, IntPtr output, out int count);
[DllImport(libX11Randr)]
public static extern int XRRGetOutputProperty(IntPtr dpy, IntPtr output, IntPtr atom, int offset, int length, bool _delete, bool pending, IntPtr req_type, out IntPtr actual_type, out int actual_format, out int nitems, out long bytes_after, out IntPtr prop);
[DllImport(libX11Randr)]
public static extern void XRRSelectInput(IntPtr dpy, IntPtr window, RandrEventMask mask);
[DllImport(libXInput)]
public static extern Status XIQueryVersion(IntPtr dpy, ref int major, ref int minor);
[DllImport(libXInput)]
public static extern IntPtr XIQueryDevice(IntPtr dpy, int deviceid, out int ndevices_return);
[DllImport(libXInput)]
public static extern void XIFreeDeviceInfo(XIDeviceInfo* info);
public static void XISetMask(ref int mask, XiEventType ev)
{
mask |= (1 << (int)ev);
}
public static int XiEventMaskLen { get; } = 4;
public static bool XIMaskIsSet(void* ptr, int shift) =>
(((byte*)(ptr))[(shift) >> 3] & (1 << (shift & 7))) != 0;
[DllImport(libXInput)]
public static extern Status XISelectEvents(
IntPtr dpy,
IntPtr win,
XIEventMask* masks,
int num_masks
);
public static Status XiSelectEvents(IntPtr display, IntPtr window, Dictionary<int, List<XiEventType>> devices)
{
var masks = stackalloc int[devices.Count];
var emasks = stackalloc XIEventMask[devices.Count];
int c = 0;
foreach (var d in devices)
{
foreach (var ev in d.Value)
XISetMask(ref masks[c], ev);
emasks[c] = new XIEventMask
{
Mask = &masks[c],
Deviceid = d.Key,
MaskLen = XiEventMaskLen
};
c++;
}
return XISelectEvents(display, window, emasks, devices.Count);
}
public struct XGeometry
{
public IntPtr root;
public int x;
public int y;
public int width;
public int height;
public int bw;
public int d;
}
public static bool XGetGeometry(IntPtr display, IntPtr window, out XGeometry geo)
{
geo = new XGeometry();
return XGetGeometry(display, window, out geo.root, out geo.x, out geo.y, out geo.width, out geo.height,
out geo.bw, out geo.d);
}
public static void QueryPointer(IntPtr display, IntPtr w, out IntPtr root, out IntPtr child,
out int root_x, out int root_y, out int child_x, out int child_y,
out int mask)
{
IntPtr c;
XGrabServer(display);
XQueryPointer(display, w, out root, out c,
out root_x, out root_y, out child_x, out child_y,
out mask);
if (root != w)
c = root;
IntPtr child_last = IntPtr.Zero;
while (c != IntPtr.Zero)
{
child_last = c;
XQueryPointer(display, c, out root, out c,
out root_x, out root_y, out child_x, out child_y,
out mask);
}
XUngrabServer(display);
XFlush(display);
child = child_last;
}
public static (int x, int y) GetCursorPos(X11Info x11, IntPtr? handle = null)
{
IntPtr root;
IntPtr child;
int root_x;
int root_y;
int win_x;
int win_y;
int keys_buttons;
QueryPointer(x11.Display, handle ?? x11.RootWindow, out root, out child, out root_x, out root_y, out win_x, out win_y,
out keys_buttons);
if (handle != null)
{
return (win_x, win_y);
}
else
{
return (root_x, root_y);
}
}
[DllImport("libXext.so.6")]
public extern static void XShapeCombineRectangles(IntPtr display, IntPtr window, XShapeKind dest_kind, int x_off, int y_off, XRectangle[] rectangles, int n_rects, XShapeOperation op, XOrdering ordering);
//public static IntPtr CreateEventWindow(LinuxPlatform plat, Action<XEvent> handler)
//{
// var win = XCreateSimpleWindow(plat.Display, plat.Info.DefaultRootWindow,
// 0, 0, 1, 1, 0, IntPtr.Zero, IntPtr.Zero);
// plat.Windows[win] = handler;
// return win;
//}
[StructLayout(LayoutKind.Explicit)]
public struct epoll_data
{
[FieldOffset(0)]
public IntPtr ptr;
[FieldOffset(0)]
public int fd;
[FieldOffset(0)]
public uint u32;
[FieldOffset(0)]
public ulong u64;
}
public const int EPOLLIN = 1;
public const int EPOLL_CTL_ADD = 1;
public const int O_NONBLOCK = 2048;
public const int O_NDELAY = (0x0004 | O_NONBLOCK);
public const int O_DIRECT = 0x100000;
[StructLayout(LayoutKind.Sequential)]
public struct epoll_event
{
public uint events;
public epoll_data data;
}
public struct Pollfd : IEquatable<Pollfd>
{
public int fd;
[CLSCompliant(false)]
public PollEvents events;
[CLSCompliant(false)]
public PollEvents revents;
public override int GetHashCode()
{
return events.GetHashCode() ^ revents.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj == null || obj.GetType() != GetType())
return false;
Pollfd value = (Pollfd)obj;
return value.events == events && value.revents == revents;
}
public bool Equals(Pollfd value)
{
return value.events == events && value.revents == revents;
}
public static bool operator ==(Pollfd lhs, Pollfd rhs)
{
return lhs.Equals(rhs);
}
public static bool operator !=(Pollfd lhs, Pollfd rhs)
{
return !lhs.Equals(rhs);
}
}
private struct _pollfd
{
public int fd;
public short events;
public short revents;
}
[DllImport("libc", SetLastError = true, EntryPoint = "poll")]
private static extern int sys_poll([In, Out] _pollfd[] ufds, uint nfds, int timeout);
public static int poll(Pollfd[] fds, uint nfds, int timeout)
{
if (fds.Length < nfds)
throw new ArgumentOutOfRangeException("fds", "Must refer to at least `nfds' elements");
_pollfd[] send = new _pollfd[nfds];
for (int i = 0; i < send.Length; i++)
{
send[i].fd = fds[i].fd;
send[i].events = (short)fds[i].events;
}
int r = sys_poll(send, nfds, timeout);
for (int i = 0; i < send.Length; i++)
{
fds[i].revents = (PollEvents)(send[i].revents);
}
return r;
}
[DllImport("libc")]
public extern static int epoll_create1(int size);
[DllImport("libc")]
public extern static int epoll_ctl(int epfd, int op, int fd, ref epoll_event __event);
[DllImport("libc")]
public extern static int epoll_wait(int epfd, epoll_event* events, int maxevents, int timeout);
[DllImport("libc")]
public extern static int pipe2(int* fds, int flags);
[DllImport("libc")]
public extern static IntPtr write(int fd, void* buf, IntPtr count);
[DllImport("libc")]
public extern static IntPtr read(int fd, void* buf, IntPtr count);
[DllImport("libdl.so.2")]
public static extern IntPtr dlopen(string path, int flags);
[DllImport("libdl.so.2")]
public static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport("libdl.so.2")]
public static extern IntPtr dlerror();
}
public struct XrmValue
{
/// unsigned int
public uint size;
/// XPointer->char*
public IntPtr addr;
}
[Flags]
public enum PollEvents : short
{
POLLIN = 0x0001, // There is data to read
POLLPRI = 0x0002, // There is urgent data to read
POLLOUT = 0x0004, // Writing now will not block
POLLERR = 0x0008, // Error condition
POLLHUP = 0x0010, // Hung up
POLLNVAL = 0x0020, // Invalid request; fd not open
// XPG4.2 definitions (via _XOPEN_SOURCE)
POLLRDNORM = 0x0040, // Normal data may be read
POLLRDBAND = 0x0080, // Priority data may be read
POLLWRNORM = 0x0100, // Writing now will not block
POLLWRBAND = 0x0200, // Priority data may be written
}
public enum XShapeOperation
{
ShapeSet,
ShapeUnion,
ShapeIntersect,
ShapeSubtract,
ShapeInvert
}
public enum XShapeKind
{
ShapeBounding,
ShapeClip,
//ShapeInput // Not usable without more imports
}
public enum XOrdering
{
Unsorted,
YSorted,
YXSorted,
YXBanded
}
}

4
CPF.Linux/license.txt Normal file
View File

@ -0,0 +1,4 @@
1、CPFCross platform UI framework版权归福州初心网络科技有限公司所有
2、未经书面同意不得向第三方泄露、给予或转让源码
3、可以修改二次开发用于一般商业软件开发。但是不允许作为控件库、UI框架类型的产品对外发布。
4、新增代码著作权归新增代码的作者所有。

66
CPF.Mac/AppDelegate.cs Normal file
View File

@ -0,0 +1,66 @@
using CPF.Mac.AppKit;
using CPF.Mac.Foundation;
using CPF.Mac.CoreGraphics;
using CPF.Controls;
using CPF;
using CPF.Drawing;
using System.Collections.Generic;
using System;
using System.Linq;
namespace CPF.Mac
{
//[Register("AppDelegate",true)]
//[Model]
public class AppDelegate : NSApplicationDelegate, NotMonoMac
{
public AppDelegate()
{
}
//NSWindow window;
public override void DidFinishLaunching(NSNotification notification)
{
//window = new NSWindow(new CGRect(0, 0, 400, 400), (NSWindowStyle.Closable | NSWindowStyle.Miniaturizable | NSWindowStyle.Resizable | NSWindowStyle.Titled), NSBackingStore.Retained, false);
//window.AcceptsMouseMovedEvents = true;
//window.WindowShouldClose = delegate
//{
// return true;
//};
//window.MakeKeyAndOrderFront(this);
}
//public override void WillTerminate(NSNotification notification)
//{
// // Insert code here to tear down your application
//}
public static List<WindowImpl> windows = new List<WindowImpl>();
public override void ScreenParametersChanged(NSNotification notification)
{
//base.ScreenParametersChanged(notification);
//System.Diagnostics.Debug.WriteLine(notification.Description);
foreach (var item in windows)
{
item.InvalidateDpi();
}
}
public override bool ApplicationShouldTerminateAfterLastWindowClosed(NSApplication sender)
{
Console.WriteLine("ApplicationShouldTerminateAfterLastWindowClosed");
var main = windows.FirstOrDefault(a => a.IsMain);
if (main != null && main.isMainVisible)
{
return false;
}
return true;
}
}
interface NotMonoMac
{
}
}

281
CPF.Mac/CPF.Mac.csproj Normal file
View File

@ -0,0 +1,281 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Product>QQ:761716178 跨平台UI框架</Product>
<Version>0.9.6.5</Version>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>key.pfx</AssemblyOriginatorKeyFile>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<PackageId>Xhm.CPF.Mac</PackageId>
<Description>CPF(Cross platform UI framework) QQ:761716178 跨平台UI框架 http://cpf.cskin.net/</Description>
<Copyright>Copyright (c) 2020 by http://cpf.cskin.net/</Copyright>
<PackageReleaseNotes>CPF(Cross platform UI framework) QQ:761716178 跨平台UI框架 http://cpf.cskin.net/</PackageReleaseNotes>
<Authors>QQ:761716178</Authors>
<Company>QQ:761716178</Company>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>D:\xhm\Documents\Visual Studio 2019\ConsoleApp1\CPF.Mac\CPF.Mac.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Mac\CoreAnimation\**" />
<Compile Remove="Mac\CoreMedia\**" />
<Compile Remove="Mac\CoreText\**" />
<Compile Remove="Mac\ImageIO\**" />
<Compile Remove="Mac\ImageKit\**" />
<EmbeddedResource Remove="Mac\CoreAnimation\**" />
<EmbeddedResource Remove="Mac\CoreMedia\**" />
<EmbeddedResource Remove="Mac\CoreText\**" />
<EmbeddedResource Remove="Mac\ImageIO\**" />
<EmbeddedResource Remove="Mac\ImageKit\**" />
<None Remove="Mac\CoreAnimation\**" />
<None Remove="Mac\CoreMedia\**" />
<None Remove="Mac\CoreText\**" />
<None Remove="Mac\ImageIO\**" />
<None Remove="Mac\ImageKit\**" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Mac\AppKit\NSAlert.cs" />
<Compile Remove="Mac\AppKit\NSAlertButtonReturn.cs" />
<Compile Remove="Mac\AppKit\NSAlertDelegate.cs" />
<Compile Remove="Mac\AppKit\NSAlertDidEndDispatcher.cs" />
<Compile Remove="Mac\AppKit\NSAlertPredicate.cs" />
<Compile Remove="Mac\AppKit\NSAlertStyle.cs" />
<Compile Remove="Mac\AppKit\NSAlertType.cs" />
<Compile Remove="Mac\AppKit\NSBrowser.cs" />
<Compile Remove="Mac\AppKit\NSBrowserCell.cs" />
<Compile Remove="Mac\AppKit\NSBrowserColumnResizingType.cs" />
<Compile Remove="Mac\AppKit\NSBrowserDelegate.cs" />
<Compile Remove="Mac\AppKit\NSBrowserDropOperation.cs" />
<Compile Remove="Mac\AppKit\NSColorPanel.cs" />
<Compile Remove="Mac\AppKit\NSColorPanelFlags.cs" />
<Compile Remove="Mac\AppKit\NSColorPanelMode.cs" />
<Compile Remove="Mac\AppKit\NSColorPicker.cs" />
<Compile Remove="Mac\AppKit\NSDatePicker.cs" />
<Compile Remove="Mac\AppKit\NSDatePickerCell.cs" />
<Compile Remove="Mac\AppKit\NSDatePickerCellDelegate.cs" />
<Compile Remove="Mac\AppKit\NSDatePickerElementFlags.cs" />
<Compile Remove="Mac\AppKit\NSDatePickerMode.cs" />
<Compile Remove="Mac\AppKit\NSDatePickerStyle.cs" />
<Compile Remove="Mac\AppKit\NSDatePickerValidatorEventArgs.cs" />
<Compile Remove="Mac\AppKit\NSDocument.cs" />
<Compile Remove="Mac\AppKit\NSDocumentChangeType.cs" />
<Compile Remove="Mac\AppKit\NSDocumentCompletionHandler.cs" />
<Compile Remove="Mac\AppKit\NSDocumentController.cs" />
<Compile Remove="Mac\AppKit\NSPopUpArrowPosition.cs" />
<Compile Remove="Mac\AppKit\NSPopUpButton.cs" />
<Compile Remove="Mac\AppKit\NSPopUpButtonCell.cs" />
<Compile Remove="Mac\AppKit\NSPredicateEditor.cs" />
<Compile Remove="Mac\AppKit\NSPredicateEditorRowTemplate.cs" />
<Compile Remove="Mac\AppKit\NSSearchField.cs" />
<Compile Remove="Mac\AppKit\NSSearchFieldCell.cs" />
<Compile Remove="Mac\AppKit\NSSecureTextField.cs" />
<Compile Remove="Mac\AppKit\NSSecureTextFieldCell.cs" />
<Compile Remove="Mac\AppKit\NSSegmentedCell.cs" />
<Compile Remove="Mac\AppKit\NSSegmentedControl.cs" />
<Compile Remove="Mac\AppKit\NSSegmentStyle.cs" />
<Compile Remove="Mac\AppKit\NSSegmentSwitchTracking.cs" />
<Compile Remove="Mac\AppKit\NSSharingService.cs" />
<Compile Remove="Mac\AppKit\NSSharingServiceDelegate.cs" />
<Compile Remove="Mac\AppKit\NSSharingServiceDidFailToShareItemsEventArgs.cs" />
<Compile Remove="Mac\AppKit\NSSharingServiceItemsEventArgs.cs" />
<Compile Remove="Mac\AppKit\NSSharingServiceName.cs" />
<Compile Remove="Mac\AppKit\NSSharingServicePicker.cs" />
<Compile Remove="Mac\AppKit\NSSharingServicePickerDelegate.cs" />
<Compile Remove="Mac\AppKit\NSSharingServicePickerDelegateForSharingService.cs" />
<Compile Remove="Mac\AppKit\NSSharingServicePickerDidChooseSharingServiceEventArgs.cs" />
<Compile Remove="Mac\AppKit\NSSharingServicePickerSharingServicesForItems.cs" />
<Compile Remove="Mac\AppKit\NSSharingServiceSourceFrameOnScreenForShareItem.cs" />
<Compile Remove="Mac\AppKit\NSSharingServiceSourceWindowForShareItems.cs" />
<Compile Remove="Mac\AppKit\NSSharingServiceTransitionImageForShareItem.cs" />
<Compile Remove="Mac\AppKit\NSSlider.cs" />
<Compile Remove="Mac\AppKit\NSSliderCell.cs" />
<Compile Remove="Mac\AppKit\NSSliderType.cs" />
<Compile Remove="Mac\AppKit\NSSpeechBoundary.cs" />
<Compile Remove="Mac\AppKit\NSSpeechRecognizer.cs" />
<Compile Remove="Mac\AppKit\NSSpeechRecognizerDelegate.cs" />
<Compile Remove="Mac\AppKit\NSSpeechSynthesizer.cs" />
<Compile Remove="Mac\AppKit\NSSpeechSynthesizerDelegate.cs" />
<Compile Remove="Mac\AppKit\NSSpellChecker.cs" />
<Compile Remove="Mac\AppKit\NSSplitView.cs" />
<Compile Remove="Mac\AppKit\NSSplitViewDelegate.cs" />
<Compile Remove="Mac\AppKit\NSSplitViewDividerStyle.cs" />
<Compile Remove="Mac\AppKit\NSWindowController.cs" />
<Compile Remove="Mac\AppKit\OpenDocumentCompletionHandler.cs" />
<Compile Remove="Mac\CoreGraphics\CGImageProperties.cs" />
<Compile Remove="Mac\CoreGraphics\CGImagePropertiesExif.cs" />
<Compile Remove="Mac\CoreGraphics\CGImagePropertiesGps.cs" />
<Compile Remove="Mac\CoreGraphics\CGImagePropertiesIptc.cs" />
<Compile Remove="Mac\CoreGraphics\CGImagePropertiesJfif.cs" />
<Compile Remove="Mac\CoreGraphics\CGImagePropertiesPng.cs" />
<Compile Remove="Mac\CoreGraphics\CGImagePropertiesTiff.cs" />
<Compile Remove="Mac\CoreImage\CIImageInitializationOptionsWithMetadata.cs" />
</ItemGroup>
<ItemGroup>
<None Remove="resizeeastwest.png" />
<None Remove="resizenortheastsouthwest.png" />
<None Remove="resizenorthsouth.png" />
<None Remove="resizenorthwestsoutheast.png" />
</ItemGroup>
<ItemGroup>
<Compile Include="Mac\CoreAnimation\CAAnimation.cs" />
<Compile Include="Mac\CoreAnimation\CAAnimationDelegate.cs" />
<Compile Include="Mac\CoreAnimation\CAAnimationGroup.cs" />
<Compile Include="Mac\CoreAnimation\CAAnimationStateEventArgs.cs" />
<Compile Include="Mac\CoreAnimation\CAAutoresizingMask.cs" />
<Compile Include="Mac\CoreAnimation\CAConstraint.cs" />
<Compile Include="Mac\CoreAnimation\CAConstraintAttribute.cs" />
<Compile Include="Mac\CoreAnimation\CAConstraintLayoutManager.cs" />
<Compile Include="Mac\CoreAnimation\CAEdgeAntialiasingMask.cs" />
<Compile Include="Mac\CoreAnimation\CALayer.cs" />
<Compile Include="Mac\CoreAnimation\CALayerDelegate.cs" />
<Compile Include="Mac\CoreAnimation\CAMediaTimingFunction.cs" />
<Compile Include="Mac\CoreAnimation\CATransform3D.cs" />
<Compile Include="Mac\CoreAnimation\CATransformLayer.cs" />
<Compile Include="Mac\CoreMedia\CMTime.cs" />
<Compile Include="Mac\CoreMedia\CMTimeMapping.cs" />
<Compile Include="Mac\CoreMedia\CMTimeRange.cs" />
<Compile Include="Mac\CoreMedia\CMTimeRoundingMethod.cs" />
<Compile Include="Mac\CoreText\Adapter.cs" />
<Compile Include="Mac\CoreText\ConstructorError.cs" />
<Compile Include="Mac\CoreText\CTBaselineClass.cs" />
<Compile Include="Mac\CoreText\CTBaselineClassID.cs" />
<Compile Include="Mac\CoreText\CTBaselineFondID.cs" />
<Compile Include="Mac\CoreText\CTBaselineFont.cs" />
<Compile Include="Mac\CoreText\CTCharacterCollection.cs" />
<Compile Include="Mac\CoreText\CTFont.cs" />
<Compile Include="Mac\CoreText\CTFontCollection.cs" />
<Compile Include="Mac\CoreText\CTFontCollectionOptionKey.cs" />
<Compile Include="Mac\CoreText\CTFontCollectionOptions.cs" />
<Compile Include="Mac\CoreText\CTFontDescriptor.cs" />
<Compile Include="Mac\CoreText\CTFontDescriptorAttributeKey.cs" />
<Compile Include="Mac\CoreText\CTFontDescriptorAttributes.cs" />
<Compile Include="Mac\CoreText\CTFontDescriptorMatchingState.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureAllTypographicFeatures.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureAlternateKana.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureAnnotation.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureCaseSensitiveLayout.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureCharacterAlternatives.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureCharacterShape.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureCJKRomanSpacing.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureCJKSymbolAlternatives.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureCJKVerticalRomanPlacement.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureContextualAlternates.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureCursiveConnection.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureDesignComplexity.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureDiacritics.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureFractions.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureIdeographicAlternatives.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureIdeographicSpacing.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureItalicCJKRoman.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureKanaSpacing.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureKey.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureLetterCase.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureLigatures.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureLinguisticRearrangementConnection.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureLowerCase.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureMathematicalExtras.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureNumberCase.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureNumberSpacing.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureOrnamentSets.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureOverlappingCharacters.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureRubyKana.cs" />
<Compile Include="Mac\CoreText\CTFontFeatures.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureSelectorKey.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureSelectors.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureSettings.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureSmartSwash.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureStyleOptions.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureStylisticAlternatives.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureTextSpacing.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureTransliteration.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureTypographicExtras.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureUnicodeDecomposition.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureUpperCase.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureVerticalPosition.cs" />
<Compile Include="Mac\CoreText\CTFontFeatureVerticalSubstitutionConnection.cs" />
<Compile Include="Mac\CoreText\CTFontFormat.cs" />
<Compile Include="Mac\CoreText\CTFontManager.cs" />
<Compile Include="Mac\CoreText\CTFontManagerAutoActivation.cs" />
<Compile Include="Mac\CoreText\CTFontManagerError.cs" />
<Compile Include="Mac\CoreText\CTFontManagerScope.cs" />
<Compile Include="Mac\CoreText\CTFontNameKey.cs" />
<Compile Include="Mac\CoreText\CTFontNameKeyId.cs" />
<Compile Include="Mac\CoreText\CTFontOptions.cs" />
<Compile Include="Mac\CoreText\CTFontOrientation.cs" />
<Compile Include="Mac\CoreText\CTFontPriority.cs" />
<Compile Include="Mac\CoreText\CTFontStylisticClass.cs" />
<Compile Include="Mac\CoreText\CTFontSymbolicTraits.cs" />
<Compile Include="Mac\CoreText\CTFontTable.cs" />
<Compile Include="Mac\CoreText\CTFontTableOptions.cs" />
<Compile Include="Mac\CoreText\CTFontTraitKey.cs" />
<Compile Include="Mac\CoreText\CTFontTraits.cs" />
<Compile Include="Mac\CoreText\CTFontUIFontType.cs" />
<Compile Include="Mac\CoreText\CTFontVariation.cs" />
<Compile Include="Mac\CoreText\CTFontVariationAxes.cs" />
<Compile Include="Mac\CoreText\CTFontVariationAxisKey.cs" />
<Compile Include="Mac\CoreText\CTFrame.cs" />
<Compile Include="Mac\CoreText\CTFrameAttributeKey.cs" />
<Compile Include="Mac\CoreText\CTFrameAttributes.cs" />
<Compile Include="Mac\CoreText\CTFramePathFillRule.cs" />
<Compile Include="Mac\CoreText\CTFrameProgression.cs" />
<Compile Include="Mac\CoreText\CTFramesetter.cs" />
<Compile Include="Mac\CoreText\CTGlyphInfo.cs" />
<Compile Include="Mac\CoreText\CTLigatureFormation.cs" />
<Compile Include="Mac\CoreText\CTLine.cs" />
<Compile Include="Mac\CoreText\CTLineBoundsOptions.cs" />
<Compile Include="Mac\CoreText\CTLineBreakMode.cs" />
<Compile Include="Mac\CoreText\CTLineTruncation.cs" />
<Compile Include="Mac\CoreText\CTParagraphStyle.cs" />
<Compile Include="Mac\CoreText\CTParagraphStyleSetting.cs" />
<Compile Include="Mac\CoreText\CTParagraphStyleSettings.cs" />
<Compile Include="Mac\CoreText\CTParagraphStyleSettingValue.cs" />
<Compile Include="Mac\CoreText\CTParagraphStyleSpecifier.cs" />
<Compile Include="Mac\CoreText\CTParagraphStyleSpecifierByteValue.cs" />
<Compile Include="Mac\CoreText\CTParagraphStyleSpecifierIntPtrsValue.cs" />
<Compile Include="Mac\CoreText\CTParagraphStyleSpecifierSingleValue.cs" />
<Compile Include="Mac\CoreText\CTParagraphStyleSpecifierValue.cs" />
<Compile Include="Mac\CoreText\CTRun.cs" />
<Compile Include="Mac\CoreText\CTRunDelegate.cs" />
<Compile Include="Mac\CoreText\CTRunDelegateCallbacks.cs" />
<Compile Include="Mac\CoreText\CTRunDelegateDeallocateCallback.cs" />
<Compile Include="Mac\CoreText\CTRunDelegateGetAscentCallback.cs" />
<Compile Include="Mac\CoreText\CTRunDelegateGetDescentCallback.cs" />
<Compile Include="Mac\CoreText\CTRunDelegateGetWidthCallback.cs" />
<Compile Include="Mac\CoreText\CTRunDelegateOperations.cs" />
<Compile Include="Mac\CoreText\CTRunDelegateVersion.cs" />
<Compile Include="Mac\CoreText\CTRunStatus.cs" />
<Compile Include="Mac\CoreText\CTStringAttributeKey.cs" />
<Compile Include="Mac\CoreText\CTStringAttributes.cs" />
<Compile Include="Mac\CoreText\CTSuperscriptStyle.cs" />
<Compile Include="Mac\CoreText\CTTextAlignment.cs" />
<Compile Include="Mac\CoreText\CTTextTab.cs" />
<Compile Include="Mac\CoreText\CTTextTabOptionKey.cs" />
<Compile Include="Mac\CoreText\CTTextTabOptions.cs" />
<Compile Include="Mac\CoreText\CTTypesetter.cs" />
<Compile Include="Mac\CoreText\CTTypesetterOptionKey.cs" />
<Compile Include="Mac\CoreText\CTTypesetterOptions.cs" />
<Compile Include="Mac\CoreText\CTUnderlineStyle.cs" />
<Compile Include="Mac\CoreText\CTUnderlineStyleModifiers.cs" />
<Compile Include="Mac\CoreText\CTWritingDirection.cs" />
<Compile Include="Mac\CoreText\FontFeatureGroup.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="resizeeastwest.png" />
<EmbeddedResource Include="resizenortheastsouthwest.png" />
<EmbeddedResource Include="resizenorthsouth.png" />
<EmbeddedResource Include="resizenorthwestsoutheast.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CPF\CPF.csproj" />
</ItemGroup>
</Project>

18
CPF.Mac/CPF.Mac.xml Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>CPF.Mac</name>
</assembly>
<members>
<member name="P:CPF.Mac.CPFNSApplication.IsHandlingSendEvent">
<summary>
Cef需要用的属性
</summary>
</member>
<member name="M:CPF.Mac.CPFNSApplication.SetHandlingSendEvent(System.Boolean)">
<summary>
Cef需要用的属性
</summary>
</member>
</members>
</doc>

View File

@ -0,0 +1,80 @@
using CPF.Mac.AppKit;
using CPF.Mac.Foundation;
using CPF.Mac.ObjCRuntime;
using System;
using System.Collections.Generic;
using System.Text;
namespace CPF.Mac
{
[Register("NSApplication")]
public class CPFNSApplication : NSApplication, CefAppProtocol, NotMonoMac
{
public CPFNSApplication(NSObjectFlag flag) : base(flag)
{
}
[Export("initWithCoder:")]
public CPFNSApplication(NSCoder coder) : base(coder)
{
}
public CPFNSApplication(IntPtr handle) : base(handle)
{
//Console.WriteLine("CPFNSApplication");
}
bool handlingSendEvent;
/// <summary>
/// Cef需要用的属性
/// </summary>
[Export("isHandlingSendEvent")]
public virtual bool IsHandlingSendEvent
{
get
{
//Console.WriteLine("isHandlingSendEvent");
return handlingSendEvent;
}
}
/// <summary>
/// Cef需要用的属性
/// </summary>
[Export("setHandlingSendEvent")]
public void SetHandlingSendEvent(bool handlingSendEvent)
{
this.handlingSendEvent = handlingSendEvent;
}
//public bool Test()
//{
// var isHn = Selector.GetHandle("isHandlingSendEvent");
// Console.WriteLine("isHandlingSendEvent");
// if (IsDirectBinding)
// {
// return Messaging.bool_objc_msgSend(base.Handle, isHn);
// }
// return Messaging.bool_objc_msgSendSuper(base.SuperHandle, isHn);
//}
}
[Protocol]
public interface CefAppProtocol : CrAppControlProtocol
{
}
[Protocol]
public interface CrAppProtocol
{
[Export("isHandlingSendEvent")]
bool IsHandlingSendEvent { get; }
}
[Protocol]
public interface CrAppControlProtocol : CrAppProtocol
{
[Export("setHandlingSendEvent")]
void SetHandlingSendEvent(bool handlingSendEvent);
}
}

88
CPF.Mac/ClipboardImpl.cs Normal file
View File

@ -0,0 +1,88 @@
using System;
using CPF.Platform;
using CPF.Input;
using CPF.Mac.Foundation;
using CPF.Mac.AppKit;
using CPF.Mac.CoreGraphics;
using CPF.Mac.ObjCRuntime;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using CPF.Drawing;
using System.Collections.Generic;
namespace CPF.Mac
{
class ClipboardImpl : DataObject, IClipboard
{
public ClipboardImpl() : base(NSPasteboard.GeneralPasteboard)
{
}
public void Clear()
{
NSPasteboard.GeneralPasteboard.ClearContents();
}
public void SetData(params (DataFormat, object)[] data)
{
Clear();
if (data != null)
{
foreach (var item in data)
{
switch (item.Item1)
{
case DataFormat.FileNames:
//NSPasteboard.GeneralPasteboard.SetDataForType(,NSPasteboard.NSFilenamesType);
var fs = (IEnumerable<string>)item.Item2;
var ns = new NSMutableArray();
foreach (var s in fs)
{
ns.Add(new NSString(s));
}
NSPasteboard.GeneralPasteboard.SetPropertyListForType(ns, NSPasteboard.NSFilenamesType);
break;
case DataFormat.Html:
var l = Encoding.UTF8.GetByteCount(item.Item2.ToString());
var h = Marshal.AllocHGlobal(l);
Marshal.Copy(Encoding.UTF8.GetBytes(item.Item2.ToString()), 0, h, l);
using (var nsdata = NSData.FromBytes(h, (ulong)l))
{
NSPasteboard.GeneralPasteboard.SetDataForType(nsdata, NSPasteboard.NSHtmlType);
Marshal.FreeHGlobal(h);
}
break;
case DataFormat.Image:
var img = item.Item2 as Image;
var stream = img.SaveToStream(ImageFormat.Png);
stream.Position = 0;
var im = Marshal.AllocHGlobal((int)stream.Length);
var d = new byte[stream.Length];
stream.Read(d, 0, (int)stream.Length);
Marshal.Copy(d, 0, im, (int)stream.Length);
using (var nsdata = NSData.FromBytes(im, (ulong)stream.Length))
{
NSPasteboard.GeneralPasteboard.SetDataForType(nsdata, NSPasteboard.NSPictType);
Marshal.FreeHGlobal(im);
}
break;
case DataFormat.Text:
//var l1 = Encoding.UTF8.GetByteCount(item.Item2.ToString());
//var h1 = Marshal.AllocHGlobal(l1);
//Marshal.Copy(Encoding.UTF8.GetBytes(item.Item2.ToString()), 0, h1, l1);
//using (var nsdata = NSData.FromBytes(h1, (ulong)l1))
//{
// NSPasteboard.GeneralPasteboard.SetDataForType(nsdata, NSPasteboard.NSStringType);
// Marshal.FreeHGlobal(h1);
//}
NSPasteboard.GeneralPasteboard.SetStringForType(item.Item2.ToString(), NSPasteboard.NSStringType);
break;
default:
break;
}
}
}
}
}
}

102
CPF.Mac/DataObject.cs Normal file
View File

@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Text;
using CPF.Input;
using CPF.Mac.Foundation;
using CPF.Mac.AppKit;
using CPF.Mac.CoreGraphics;
using CPF.Mac.ObjCRuntime;
using System.Linq;
using System.Xml;
namespace CPF.Mac
{
class DataObject : IDataObject
{
public DataObject(NSPasteboard pasteboard)
{
this.pasteboard = pasteboard;
}
public static readonly IntPtr AppKit_Handle = Dlfcn.dlopen("/System/Library/Frameworks/AppKit.framework/AppKit", 0);
private static NSString _NSPasteboardTypeString;
public static NSString NSPasteboardTypeString
{
get
{
if (_NSPasteboardTypeString == null)
{
_NSPasteboardTypeString = Dlfcn.GetStringConstant(AppKit_Handle, "NSPasteboardTypeString");
}
return _NSPasteboardTypeString;
}
}
protected NSPasteboard pasteboard;
public bool Contains(DataFormat dataFormat)
{
string type = NSPasteboardTypeString;
if (dataFormat == DataFormat.FileNames)
{
type = NSPasteboard.NSFilenamesType;
}
else if (dataFormat == DataFormat.Html)
{
type = NSPasteboard.NSHtmlType;
}
else if (dataFormat == DataFormat.Image)
{
type = NSPasteboard.NSPictType;
}
//var r = pasteboard.CanReadItemWithDataConformingToTypes(new string[] { type });
return pasteboard.Types.Any(a => a == type);
}
public object GetData(DataFormat dataFormat)
{
switch (dataFormat)
{
case DataFormat.FileNames:
List<string> fileNames = new List<string>();
//var xml = pasteboard.GetDataForType(NSPasteboard.NSFilenamesType);
//XmlDocument xmldoc = new XmlDocument();
//xmldoc.LoadXml(xml.ToString());
//foreach (XmlNode item in xmldoc.ChildNodes)
//{
// if (item.Name == "plist" && item.NodeType == XmlNodeType.Element)
// {
// foreach (XmlNode array in item.ChildNodes)
// {
// if (array.Name == "array" && item.NodeType == XmlNodeType.Element)
// {
// foreach (XmlNode str in array)
// {
// fileNames.Add(str.InnerText);
// }
// break;
// }
// }
// break;
// }
//}
var ns = (NSMutableArray)pasteboard.GetPropertyListForType(NSPasteboard.NSFilenamesType);
for (int i = 0; i < (int)ns.Count; i++)
{
fileNames.Add(new NSString(ns.ValueAt((ulong)i)).ToString());
}
return fileNames;
case DataFormat.Html:
var html= pasteboard.GetDataForType(NSPasteboard.NSHtmlType)?.ToString();
return html;
case DataFormat.Image:
return pasteboard.GetDataForType(NSPasteboard.NSPictType);
case DataFormat.Text:
return pasteboard.GetDataForType(NSPasteboard.NSStringType)?.ToString();
default:
break;
}
return null;
}
}
}

247
CPF.Mac/Key.cs Normal file
View File

@ -0,0 +1,247 @@
using System;
using CPF.Input;
using System.Collections.Generic;
namespace CPF.Mac
{
public class Key
{
const int kVK_ANSI_A = 0x00;
const int kVK_ANSI_S = 0x01;
const int kVK_ANSI_D = 0x02;
const int kVK_ANSI_F = 0x03;
const int kVK_ANSI_H = 0x04;
const int kVK_ANSI_G = 0x05;
const int kVK_ANSI_Z = 0x06;
const int kVK_ANSI_X = 0x07;
const int kVK_ANSI_C = 0x08;
const int kVK_ANSI_V = 0x09;
const int kVK_ANSI_B = 0x0B;
const int kVK_ANSI_Q = 0x0C;
const int kVK_ANSI_W = 0x0D;
const int kVK_ANSI_E = 0x0E;
const int kVK_ANSI_R = 0x0F;
const int kVK_ANSI_Y = 0x10;
const int kVK_ANSI_T = 0x11;
const int kVK_ANSI_1 = 0x12;
const int kVK_ANSI_2 = 0x13;
const int kVK_ANSI_3 = 0x14;
const int kVK_ANSI_4 = 0x15;
const int kVK_ANSI_6 = 0x16;
const int kVK_ANSI_5 = 0x17;
//const int kVK_ANSI_Equal = 0x18;
const int kVK_ANSI_9 = 0x19;
const int kVK_ANSI_7 = 0x1A;
const int kVK_ANSI_Minus = 0x1B;
const int kVK_ANSI_8 = 0x1C;
const int kVK_ANSI_0 = 0x1D;
const int kVK_ANSI_RightBracket = 0x1E;
const int kVK_ANSI_O = 0x1F;
const int kVK_ANSI_U = 0x20;
const int kVK_ANSI_LeftBracket = 0x21;
const int kVK_ANSI_I = 0x22;
const int kVK_ANSI_P = 0x23;
const int kVK_ANSI_L = 0x25;
const int kVK_ANSI_J = 0x26;
const int kVK_ANSI_Quote = 0x27;
const int kVK_ANSI_K = 0x28;
const int kVK_ANSI_Semicolon = 0x29;
const int kVK_ANSI_Backslash = 0x2A;
const int kVK_ANSI_Comma = 0x2B;
//const int kVK_ANSI_Slash = 0x2C;
const int kVK_ANSI_N = 0x2D;
const int kVK_ANSI_M = 0x2E;
const int kVK_ANSI_Period = 0x2F;
//const int kVK_ANSI_Grave = 0x32;
const int kVK_ANSI_KeypadDecimal = 0x41;
const int kVK_ANSI_KeypadMultiply = 0x43;
const int kVK_ANSI_KeypadPlus = 0x45;
const int kVK_ANSI_KeypadClear = 0x47;
const int kVK_ANSI_KeypadDivide = 0x4B;
const int kVK_ANSI_KeypadEnter = 0x4C;
const int kVK_ANSI_KeypadMinus = 0x4E;
//const int kVK_ANSI_KeypadEquals = 0x51;
const int kVK_ANSI_Keypad0 = 0x52;
const int kVK_ANSI_Keypad1 = 0x53;
const int kVK_ANSI_Keypad2 = 0x54;
const int kVK_ANSI_Keypad3 = 0x55;
const int kVK_ANSI_Keypad4 = 0x56;
const int kVK_ANSI_Keypad5 = 0x57;
const int kVK_ANSI_Keypad6 = 0x58;
const int kVK_ANSI_Keypad7 = 0x59;
const int kVK_ANSI_Keypad8 = 0x5B;
const int kVK_ANSI_Keypad9 = 0x5C;
const int kVK_Return = 0x24;
const int kVK_Tab = 0x30;
const int kVK_Space = 0x31;
internal const int kVK_Delete = 0x33;
const int kVK_Escape = 0x35;
const int kVK_Command = 0x37;
const int kVK_Shift = 0x38;
const int kVK_CapsLock = 0x39;
const int kVK_Option = 0x3A;
const int kVK_Control = 0x3B;
const int kVK_RightCommand = 0x36;
const int kVK_RightShift = 0x3C;
const int kVK_RightOption = 0x3D;
const int kVK_RightControl = 0x3E;
//const int kVK_Function = 0x3F;
const int kVK_F17 = 0x40;
const int kVK_VolumeUp = 0x48;
const int kVK_VolumeDown = 0x49;
const int kVK_Mute = 0x4A;
const int kVK_F18 = 0x4F;
const int kVK_F19 = 0x50;
const int kVK_F20 = 0x5A;
const int kVK_F5 = 0x60;
const int kVK_F6 = 0x61;
const int kVK_F7 = 0x62;
const int kVK_F3 = 0x63;
const int kVK_F8 = 0x64;
const int kVK_F9 = 0x65;
const int kVK_F11 = 0x67;
const int kVK_F13 = 0x69;
const int kVK_F16 = 0x6A;
const int kVK_F14 = 0x6B;
const int kVK_F10 = 0x6D;
const int kVK_F12 = 0x6F;
const int kVK_F15 = 0x71;
const int kVK_Help = 0x72;
const int kVK_Home = 0x73;
const int kVK_PageUp = 0x74;
const int kVK_ForwardDelete = 0x75;
const int kVK_F4 = 0x76;
const int kVK_End = 0x77;
const int kVK_F2 = 0x78;
const int kVK_PageDown = 0x79;
const int kVK_F1 = 0x7A;
const int kVK_LeftArrow = 0x7B;
const int kVK_RightArrow = 0x7C;
const int kVK_DownArrow = 0x7D;
const int kVK_UpArrow = 0x7E;
//const int kVK_ISO_Section = 0x0A;
//const int kVK_JIS_Yen = 0x5D;
//const int kVK_JIS_Underscore = 0x5E;
//const int kVK_JIS_KeypadComma = 0x5F;
//const int kVK_JIS_Eisu = 0x66;
//const int kVK_JIS_Kana = 0x68;
public static Dictionary<int, Keys> s_KeyMap = new Dictionary<int, Keys>
{
{kVK_ANSI_A, Keys.A},
{kVK_ANSI_S, Keys.S},
{kVK_ANSI_D, Keys.D},
{kVK_ANSI_F, Keys.F},
{kVK_ANSI_H, Keys.H},
{kVK_ANSI_G, Keys.G},
{kVK_ANSI_Z, Keys.Z},
{kVK_ANSI_X, Keys.X},
{kVK_ANSI_C, Keys.C},
{kVK_ANSI_V, Keys.V},
{kVK_ANSI_B, Keys.B},
{kVK_ANSI_Q, Keys.Q},
{kVK_ANSI_W, Keys.W},
{kVK_ANSI_E, Keys.E},
{kVK_ANSI_R, Keys.R},
{kVK_ANSI_Y, Keys.Y},
{kVK_ANSI_T, Keys.T},
{kVK_ANSI_1, Keys.D1},
{kVK_ANSI_2, Keys.D2},
{kVK_ANSI_3, Keys.D3},
{kVK_ANSI_4, Keys.D4},
{kVK_ANSI_6, Keys.D6},
{kVK_ANSI_5, Keys.D5},
//{kVK_ANSI_EKeys.qual, ?},
{kVK_ANSI_9, Keys.D9},
{kVK_ANSI_7, Keys.D7},
{kVK_ANSI_Minus, Keys.OemMinus},
{kVK_ANSI_8, Keys.D8},
{kVK_ANSI_0, Keys.D0},
{kVK_ANSI_RightBracket, Keys.OemCloseBrackets},
{kVK_ANSI_O, Keys.O},
{kVK_ANSI_U, Keys.U},
{kVK_ANSI_LeftBracket, Keys.OemOpenBrackets},
{kVK_ANSI_I, Keys.I},
{kVK_ANSI_P, Keys.P},
{kVK_ANSI_L, Keys.L},
{kVK_ANSI_J, Keys.J},
{kVK_ANSI_Quote, Keys.OemQuotes},
{kVK_ANSI_K, Keys.K},
{kVK_ANSI_Semicolon, Keys.OemSemicolon},
{kVK_ANSI_Backslash, Keys.OemBackslash},
{kVK_ANSI_Comma, Keys.OemComma},
//{kVK_ANSI_Slash, ?},
{kVK_ANSI_N, Keys.N},
{kVK_ANSI_M, Keys.M},
{kVK_ANSI_Period, Keys.OemPeriod},
//{kVK_ANSI_Grave, ?},
{kVK_ANSI_KeypadDecimal, Keys.Decimal},
{kVK_ANSI_KeypadMultiply, Keys.Multiply},
{kVK_ANSI_KeypadPlus,Keys. OemPlus},
{kVK_ANSI_KeypadClear, Keys.Clear},
{kVK_ANSI_KeypadDivide, Keys.Divide},
{kVK_ANSI_KeypadEnter, Keys.Enter},
{kVK_ANSI_KeypadMinus, Keys.OemMinus},
//{kVK_ANSI_KeypadEquals, ?},
{kVK_ANSI_Keypad0, Keys.NumPad0},
{kVK_ANSI_Keypad1, Keys.NumPad1},
{kVK_ANSI_Keypad2, Keys.NumPad2},
{kVK_ANSI_Keypad3, Keys.NumPad3},
{kVK_ANSI_Keypad4, Keys.NumPad4},
{kVK_ANSI_Keypad5, Keys.NumPad5},
{kVK_ANSI_Keypad6, Keys.NumPad6},
{kVK_ANSI_Keypad7, Keys.NumPad7},
{kVK_ANSI_Keypad8, Keys.NumPad8},
{kVK_ANSI_Keypad9, Keys.NumPad9},
{kVK_Return, Keys.Return},
{kVK_Tab, Keys.Tab},
{kVK_Space, Keys.Space},
{kVK_Delete, Keys.Back},
{kVK_Escape, Keys.Escape},
{kVK_Command, Keys.LWin},
{kVK_Shift, Keys.LeftShift},
{kVK_CapsLock, Keys.CapsLock},
{kVK_Option, Keys.LeftAlt},
{kVK_Control, Keys.LeftCtrl},
{kVK_RightCommand, Keys.RWin},
{kVK_RightShift, Keys.RightShift},
{kVK_RightOption, Keys.RightAlt},
{kVK_RightControl, Keys.RightCtrl},
//{kVK_Function, ?},
{kVK_F17, Keys.F17},
{kVK_VolumeUp, Keys.VolumeUp},
{kVK_VolumeDown, Keys.VolumeDown},
{kVK_Mute, Keys.VolumeMute},
{kVK_F18, Keys.F18},
{kVK_F19, Keys.F19},
{kVK_F20, Keys.F20},
{kVK_F5, Keys.F5},
{kVK_F6, Keys.F6},
{kVK_F7, Keys.F7},
{kVK_F3, Keys.F3},
{kVK_F8, Keys.F8},
{kVK_F9, Keys.F9},
{kVK_F11, Keys.F11},
{kVK_F13, Keys.F13},
{kVK_F16, Keys.F16},
{kVK_F14, Keys.F14},
{kVK_F10, Keys.F10},
{kVK_F12, Keys.F12},
{kVK_Help, Keys.Help},
{kVK_Home, Keys.Home},
{kVK_PageUp, Keys.PageUp},
{kVK_ForwardDelete, Keys.Delete},
{kVK_F4, Keys.F4},
{kVK_F2, Keys.F2},
{kVK_F1, Keys.F1},
{kVK_F15,Keys.F15},
{kVK_End, Keys.End},
{kVK_PageDown, Keys.PageDown},
{kVK_LeftArrow, Keys.Left},
{kVK_RightArrow, Keys.Right},
{kVK_DownArrow, Keys.Down},
{kVK_UpArrow, Keys.Up}
};
}
}

Some files were not shown because too many files have changed in this diff Show More