Anybody has a clue on how to use the ExternalAccessory API on Xamarin.iOS?
My Xamarin Studio version is 4.0.12(build 3), Xamarin.Android version 4.8.1, Xamarin.iOS version 6.4.5.0 and Xcode is Version 5.0 (5A1413) and I tried target both 6.1 and 7.0 iPad/iPhone.
I've walked the internet and there is not much documentation. Even the MonoTouch docs have broken links.
What I want is, list the connected bluetooth devices, get one of then by name and then connect to it so I can open a socket and start sending data to it. It is a device that uses Serial communication and yes, it has the Apple external accessory protocol ID.
I've tried this:
var am = EAAccessoryManager.SharedAccessoryManager;
It just throws me an exception an InvaidCastException.
Any clues?
Thanks! I really appreciate the help.
PS: Xamarin Details
Xamarin Studio
Version 4.0.12 (build 3)
Installation UUID: 7348d641-ed6d-4c8a-b59a-116674e06dfd
Runtime:
Mono 3.2.0 ((no/7c7fcc7)
GTK 2.24.20
GTK# (2.12.0.0)
Package version: 302000000
[...]
Apple Developer Tools
Xcode 5.0 (3332.25)
Build 5A1413
[...]
Xamarin.iOS
Version: 6.4.5.0 (Trial Edition)
Hash: 1336a36
Branch:
Build date: 2013-10-09 11:14:45-0400
Build Information
Release ID: 400120003
Git revision: 593d7acb1cb78ceeeb482d5133cf1fe514467e39
Build date: 2013-08-07 20:30:53+0000
Xamarin addins: 25a0858b281923e666b09259ad4746b774e0a873
Operating System
Mac OS X 10.8.5
Darwin Gutembergs-MacBook-Pro.local 12.5.0 Darwin Kernel Version 12.5.0
Mon Jul 29 16:33:49 PDT 2013
root:xnu-2050.48.11~1/RELEASE_X86_64 x86_64
Although it seems like you've worked this out, I thought I'd show some code snippets that show the basics (in this case connecting to a Sphero and turning it green):
EAAccessoryManager mgr = EAAccessoryManager.SharedAccessoryManager;
var accessories = mgr.ConnectedAccessories;
foreach(var accessory in accessories)
{
myLabel.Text = "Got me an accessory";
Console.WriteLine(accessory.ToString());
Console.WriteLine(accessory.Name);
var protocol = "com.orbotix.robotprotocol";
if(accessory.ProtocolStrings.Where(s => s == protocol).Any())
{
myLabel.Text = "Got me a Sphero";
var session = new EASession(accessory, protocol);
var outputStream = session.OutputStream;
outputStream.Delegate = new MyOutputStreamDelegate(myLabel);
outputStream.Schedule(NSRunLoop.Current, "kCFRunLoopDefaultMode");
outputStream.Open();
}
}
and
public class MyOutputStreamDelegate : NSStreamDelegate
{
UILabel label;
bool hasWritten = false;
public MyOutputStreamDelegate(UILabel label)
{
this.label = label;
}
public override void HandleEvent(NSStream theStream, NSStreamEvent streamEvent)
{
if(streamEvent == NSStreamEvent.HasSpaceAvailable && ! hasWritten)
{
//Set the color of the Sphero
var written = ((NSOutputStream)theStream).Write(new byte[] {0xFF, 0xFF, 0x02, 0x20, 0x0e, 0x05, 0x1F, 0xFF, 0x1B, 0x00, 0x91}, 11);
if(written == 11)
{
label.Text = "Sphero should be green";
}
hasWritten = true;
}
}
}
I know you specifically asked about writing data to the bluetooth device, but this is just expanding on reading data, as well as the general use of the External Accessory API for Xamarin.iOS because there isn't much documentation or Xamarin samples out there. This is a loose conversion from the Apple sample done with Objective-C. My accessory was a MFi certified microchip reader. I've only put in the "read" functionality since I only needed that for my app.
Create a SessionController class inheriting from NSStreamDelegate and this does a lot of the plumbing. Opens, closes sessions, handles events from the device and reads the data. You'd add your write methods here too I think.
public class EASessionController : NSStreamDelegate
{
NSString SessionDataReceivedNotification = (NSString)"SessionDataReceivedNotification";
public static EAAccessory _accessory;
public static string _protocolString;
EASession _session;
NSMutableData _readData;
public static EASessionController SharedController()
{
EASessionController sessionController = null;
if (sessionController == null)
{
sessionController = new EASessionController();
}
return sessionController;
}
public void SetupController(EAAccessory accessory, string protocolString)
{
_accessory = accessory;
_protocolString = protocolString;
}
public bool OpenSession()
{
Console.WriteLine("opening new session");
_accessory.WeakDelegate = this;
if (_session == null)
_session = new EASession(_accessory, _protocolString);
// Open both input and output streams even if the device only makes use of one of them
_session.InputStream.Delegate = this;
_session.InputStream.Schedule(NSRunLoop.Current, NSRunLoopMode.Default);
_session.InputStream.Open();
_session.OutputStream.Delegate = this;
_session.OutputStream.Schedule(NSRunLoop.Current, NSRunLoopMode.Default);
_session.OutputStream.Open();
return (_session != null);
}
public void CloseSession()
{
_session.InputStream.Unschedule(NSRunLoop.Current, NSRunLoopMode.Default);
_session.InputStream.Delegate = null;
_session.InputStream.Close();
_session.OutputStream.Unschedule(NSRunLoop.Current, NSRunLoopMode.Default);
_session.OutputStream.Delegate = null;
_session.OutputStream.Close();
_session = null;
}
/// <summary>
/// Get Number of bytes to read into local buffer
/// </summary>
/// <returns></returns>
public nuint ReadBytesAvailable()
{
return _readData.Length;
}
/// <summary>
/// High level read method
/// </summary>
/// <param name="bytesToRead"></param>
/// <returns></returns>
public NSData ReadData(nuint bytesToRead)
{
NSData data = null;
if (_readData.Length >= bytesToRead)
{
NSRange range = new NSRange(0, (nint)bytesToRead);
data = _readData.Subdata(range);
_readData.ReplaceBytes(range, IntPtr.Zero, 0);
}
return data;
}
/// <summary>
/// Low level read method - read data while there is data and space in input buffer, then post notification to observer
/// </summary>
void ReadData()
{
nuint bufferSize = 128;
byte[] buffer = new byte[bufferSize];
while (_session.InputStream.HasBytesAvailable())
{
nint bytesRead = _session.InputStream.Read(buffer, bufferSize);
if (_readData == null)
{
_readData = new NSMutableData();
}
_readData.AppendBytes(buffer, 0, bytesRead);
Console.WriteLine(buffer);
}
// We now have our data from the device (stored in _readData), so post the notification for an observer to do something with the data
NSNotificationCenter.DefaultCenter.PostNotificationName(SessionDataReceivedNotification, this);
}
/// <summary>
/// Handle the events occurring with the external accessory
/// </summary>
/// <param name="theStream"></param>
/// <param name="streamEvent"></param>
public override void HandleEvent(NSStream theStream, NSStreamEvent streamEvent)
{
switch (streamEvent)
{
case NSStreamEvent.None:
Console.WriteLine("StreamEventNone");
break;
case NSStreamEvent.HasBytesAvailable:
Console.WriteLine("StreamEventHasBytesAvailable");
ReadData();
break;
case NSStreamEvent.HasSpaceAvailable:
Console.WriteLine("StreamEventHasSpaceAvailable");
// Do write operations to the device here
break;
case NSStreamEvent.OpenCompleted:
Console.WriteLine("StreamEventOpenCompleted");
break;
case NSStreamEvent.ErrorOccurred:
Console.WriteLine("StreamEventErroOccurred");
break;
case NSStreamEvent.EndEncountered:
Console.WriteLine("StreamEventEndEncountered");
break;
default:
Console.WriteLine("Stream present but no event");
break;
}
}
}
In my ViewController that's going to display the data I've just read from the external accessory, we wire it all up. In ViewDidLoad, create observers so the view knows when an event has been fired by the device. Also check we're connected to the correct accessory and open a session.
public EASessionController _EASessionController;
EAAccessory[] _accessoryList;
EAAccessory _selectedAccessory;
NSString SessionDataReceivedNotification = (NSString)"SessionDataReceivedNotification";
string myDeviceProtocol = "com.my-microchip-reader.1234";
public override void ViewDidLoad()
{
base.ViewDidLoad();
NSNotificationCenter.DefaultCenter.AddObserver(EAAccessoryManager.DidConnectNotification, EADidConnect);
NSNotificationCenter.DefaultCenter.AddObserver(EAAccessoryManager.DidDisconnectNotification, EADidDisconnect);
NSNotificationCenter.DefaultCenter.AddObserver(SessionDataReceivedNotification, SessionDataReceived);
EAAccessoryManager.SharedAccessoryManager.RegisterForLocalNotifications();
_EASessionController = EASessionController.SharedController();
_accessoryList = EAAccessoryManager.SharedAccessoryManager.ConnectedAccessories;
foreach (EAAccessory acc in _accessoryList)
{
if (acc.ProtocolStrings.Contains(myDeviceProtocol))
{
// Connected to the correct accessory
_selectedAccessory = acc;
_EASessionController.SetupController(acc, myDeviceProtocol);
_EASessionController.OpenSession();
lblEAConnectionStatus.Text = acc.Name;
Console.WriteLine("Already connected via bluetooth");
}
else
{
// Not connected
}
}
}
Create the DidConnect, DidDisconnect and SessionDataReceived methods. The device name is just updated on some labels when connected/disconnected and I'm displaying the data in text field.
void EADidConnect(NSNotification notification)
{
EAAccessory connectedAccessory = (EAAccessory)notification.UserInfo.ObjectForKey((NSString)"EAAccessoryKey");
Console.WriteLine("I did connect!!");
_accessoryList = EAAccessoryManager.SharedAccessoryManager.ConnectedAccessories;
// Reconnect and open the session in case the device was disconnected
foreach (EAAccessory acc in _accessoryList)
{
if (acc.ProtocolStrings.Contains(myDeviceProtocol))
{
// Connected to the correct accessory
_selectedAccessory = acc;
Console.WriteLine(_selectedAccessory.ProtocolStrings);
_EASessionController.SetupController(acc, myDeviceProtocol);
_EASessionController.OpenSession();
}
else
{
// Not connected
}
}
Console.WriteLine(connectedAccessory.Name);
// Update a label to show it's connected
lblEAConnectionStatus.Text = connectedAccessory.Name;
}
void EADidDisconnect(NSNotification notification)
{
Console.WriteLine("Accessory disconnected");
_EASessionController.CloseSession();
lblEAConnectionStatus.Text = string.Empty;
}
/// <summary>
/// Data receieved from accessory
/// </summary>
/// <param name="notification"></param>
void SessionDataReceived(NSNotification notification)
{
EASessionController sessionController = (EASessionController)notification.Object;
nuint bytesAvailable = 0;
while ((bytesAvailable = sessionController.ReadBytesAvailable()) > 0)
{
// read the data as a string
NSData data = sessionController.ReadData(bytesAvailable);
NSString chipNumber = new NSString(data, NSStringEncoding.UTF8);
// Displaying the data
txtMircochipNumber.Text = chipNumber;
}
}
Related
The program returns: CANCELED: Reason=Error ErrorDetails=WebSocket Upgrade failed with an authentication error (401). Please check for correct subscription key (or authorization token) and region name. SessionId: cbfcdf7f26304343a08de6c398652053
I'm using my free trial subscription key and westus region. This is the sample code found here: https://learn.microsoft.com/en-us/azure/cognitive-services/speech-service/quickstarts/speech-to-text-from-microphone?tabs=unity%2Cx-android%2Clinux%2Cjava-runtime&pivots=programming-language-csharp
using UnityEngine;
using UnityEngine.UI;
using Microsoft.CognitiveServices.Speech;
#if PLATFORM_ANDROID
using UnityEngine.Android;
#endif
#if PLATFORM_IOS
using UnityEngine.iOS;
using System.Collections;
#endif
public class Helloworld : MonoBehaviour
{
// Hook up the two properties below with a Text and Button object in your UI.
public Text outputText;
public Button startRecoButton;
private object threadLocker = new object();
private bool waitingForReco;
private string message;
private bool micPermissionGranted = false;
#if PLATFORM_ANDROID || PLATFORM_IOS
// Required to manifest microphone permission, cf.
// https://docs.unity3d.com/Manual/android-manifest.html
private Microphone mic;
#endif
public async void ButtonClick()
{
// Creates an instance of a speech config with specified subscription key and service region.
// Replace with your own subscription key and service region (e.g., "westus").
var config = SpeechConfig.FromSubscription("yourSubscriptionKey", "yourRegion");
// Make sure to dispose the recognizer after use!
using (var recognizer = new SpeechRecognizer(config))
{
lock (threadLocker)
{
waitingForReco = true;
}
// Starts speech recognition, and returns after a single utterance is recognized. The end of a
// single utterance is determined by listening for silence at the end or until a maximum of 15
// seconds of audio is processed. The task returns the recognition text as result.
// Note: Since RecognizeOnceAsync() returns only a single utterance, it is suitable only for single
// shot recognition like command or query.
// For long-running multi-utterance recognition, use StartContinuousRecognitionAsync() instead.
var result = await recognizer.RecognizeOnceAsync().ConfigureAwait(false);
// Checks result.
string newMessage = string.Empty;
if (result.Reason == ResultReason.RecognizedSpeech)
{
newMessage = result.Text;
}
else if (result.Reason == ResultReason.NoMatch)
{
newMessage = "NOMATCH: Speech could not be recognized.";
}
else if (result.Reason == ResultReason.Canceled)
{
var cancellation = CancellationDetails.FromResult(result);
newMessage = $"CANCELED: Reason={cancellation.Reason} ErrorDetails={cancellation.ErrorDetails}";
}
lock (threadLocker)
{
message = newMessage;
waitingForReco = false;
}
}
}
void Start()
{
if (outputText == null)
{
UnityEngine.Debug.LogError("outputText property is null! Assign a UI Text element to it.");
}
else if (startRecoButton == null)
{
message = "startRecoButton property is null! Assign a UI Button to it.";
UnityEngine.Debug.LogError(message);
}
else
{
// Continue with normal initialization, Text and Button objects are present.
#if PLATFORM_ANDROID
// Request to use the microphone, cf.
// https://docs.unity3d.com/Manual/android-RequestingPermissions.html
message = "Waiting for mic permission";
if (!Permission.HasUserAuthorizedPermission(Permission.Microphone))
{
Permission.RequestUserPermission(Permission.Microphone);
}
#elif PLATFORM_IOS
if (!Application.HasUserAuthorization(UserAuthorization.Microphone))
{
Application.RequestUserAuthorization(UserAuthorization.Microphone);
}
#else
micPermissionGranted = true;
message = "Click button to recognize speech";
#endif
startRecoButton.onClick.AddListener(ButtonClick);
}
}
void Update()
{
#if PLATFORM_ANDROID
if (!micPermissionGranted && Permission.HasUserAuthorizedPermission(Permission.Microphone))
{
micPermissionGranted = true;
message = "Click button to recognize speech";
}
#elif PLATFORM_IOS
if (!micPermissionGranted && Application.HasUserAuthorization(UserAuthorization.Microphone))
{
micPermissionGranted = true;
message = "Click button to recognize speech";
}
#endif
lock (threadLocker)
{
if (startRecoButton != null)
{
startRecoButton.interactable = !waitingForReco && micPermissionGranted;
}
if (outputText != null)
{
outputText.text = message;
}
}
}
}
The sample code you pasted above still has the placeholder values for region and subscription key. Just double checking that you did in fact replace those strings with your own subscription key and region? If that's true, can you please turn on logging, run the code again, and then provide the log? We can help diagnose from there...
To turn on logging, see https://aka.ms/speech/logging.
How can i access the ViewController in my DependencyService to present a MFMailComposeViewController? I tried using Application.Context but this seems to be only working on Android. Any advice?
You can present a MFMailComposeViewController by doing a window.RootController.PresentViewController (mail controller, true, null);. Depending on your app architecture, the RootViewController might not be an usable ViewController in the hierarchy. In that case you get a
Warning: Attempt to present <MFMailComposeViewController: 0x16302c30> on <Xamarin_Forms_Platform_iOS_PlatformRenderer: 0x14fd1530> whose view is not in the window hierarchy!
In that case, you have to dig for the concrete ViewController, in my case it is:
var rootController = ((AppDelegate)(UIApplication.SharedApplication.Delegate)).Window.RootViewController.ChildViewControllers[0].ChildViewControllers[1].ChildViewControllers[0];
which is a bit wicked, but works (An issue for this have been filed for future fix).
The full solution then looks like:
in your AppDelegate.cs, add this:
public UIWindow Window {
get { return window; }
}
in your PCL project, declare the interface: ISendMailService.cs
public interface ISendMailService
{
void ComposeMail (string[] recipients, string subject, string messagebody = null, Action<bool> completed = null);
}
in your iOS project, implement and register the interface: SendMailService.cs
[assembly: DependencyAttribute(typeof(SendMailService))]
public class SendMailService : ISendMailService
{
public void ComposeMail (string[] recipients, string subject, string messagebody = null, Action<bool> completed = null)
{
var controller = new MFMailComposeViewController ();
controller.SetToRecipients (recipients);
controller.SetSubject (subject);
if (!string.IsNullOrEmpty (messagebody))
controller.SetMessageBody (messagebody, false);
controller.Finished += (object sender, MFComposeResultEventArgs e) => {
if (completed != null)
completed (e.Result == MFMailComposeResult.Sent);
e.Controller.DismissViewController (true, null);
};
//Adapt this to your app structure
var rootController = ((AppDelegate)(UIApplication.SharedApplication.Delegate)).Window.RootViewController.ChildViewControllers[0].ChildViewControllers[1].ChildViewControllers[0];
var navcontroller = rootController as UINavigationController;
if (navcontroller != null)
rootController = navcontroller.VisibleViewController;
rootController.PresentViewController (controller, true, null);
}
}
And you can now consume it from your Xamarin.Forms PCL project:
new Button {
Font = Font.SystemFontOfSize (NamedSize.Medium),
Text = "Contact us",
TextColor = Color.White,
BackgroundColor = ColorsAndStyles.LightBlue,
BorderRadius = 0,
Command = new Command (()=>{
var mailservice = DependencyService.Get<ISendMailService> ();
if (mailservice == null)
return;
mailservice.ComposeMail (new [] {"foo#example.com"}, "Test", "Hello, World");
})
}
Use: UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(controller, true, null);
I would like to add an additional answer based off of the KeyWindow not always being the main window. (this occurs when you are presenting your controller after the user has interacted with an action sheet or alert dialog)
public static UIViewController GetCurrentUIController()
{
UIViewController viewController;
var window = UIApplication.SharedApplication.KeyWindow;
if (window == null)
{
throw new InvalidOperationException("There's no current active window");
}
if (window.RootViewController.PresentedViewController == null)
{
window = UIApplication.SharedApplication.Windows
.First(i => i.RootViewController != null &&
i.RootViewController.GetType().FullName
.Contains(typeof(Xamarin.Forms.Platform.iOS.Platform).FullName));
}
viewController = window.RootViewController;
while (viewController.PresentedViewController != null)
{
viewController = viewController.PresentedViewController;
}
return viewController;
}
This will guarantee that you get the Xamarin Forms platform renderer window, then find the foremost presented ViewController and return it for use presenting whatever UI or view controller you need to present.
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(controller, true, null);
This only works in above all solutions
Just for a reference. It took me some time to figure it out how to launch it from modal window.
Here comes the solution:
var rootController = ((AppDelegate)(UIApplication.SharedApplication.Delegate)).Window.RootViewController.PresentedViewController;
var navcontroller = rootController as UINavigationController;
if (navcontroller != null)
rootController = navcontroller.VisibleViewController;
rootController.PresentViewController (controller, true, null);
Code in the main form:
private delegate bool IncreaseProbarHandler(int nIncVal); //Declare a delegate to increase the progress bar value.
private IncreaseProbarHandler _IncHanler = null;
private List<Microsoft.Win32.RegistryKey> _RKeys = new List<Microsoft.Win32.RegistryKey>(); //Store the RegistryKey.
public MainForm() {
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e) {
new Thread(ProThread).Start();
RecursiveRegedit(Microsoft.Win32.Registry.CurrentUser);
//RecursiveRegedit(Microsoft.Win32.Registry.LocalMachine);
MessageBox.Show("Done!");
}
//Recursive scan the registry.
void RecursiveRegedit(Microsoft.Win32.RegistryKey regBoot) {
if(regBoot == null) throw new ArgumentNullException("Null Item!");
string[] vals = regBoot.GetValueNames();
foreach(var v in vals) {
if(regBoot.GetValue(v) != null) {
string s = regBoot.GetValue(v).ToString();
if(s.StartsWith("C:", StringComparison.CurrentCultureIgnoreCase))
_RKeys.Add(regBoot); //Add to 'List'.
}
}
if(regBoot.SubKeyCount <= 0) //Exit.
return;
else { //Recursive.
string[] subs = regBoot.GetSubKeyNames();
foreach(string s in subs) {
try {//Try...catch the not accessible notes exception.
RecursiveRegedit(regBoot.OpenSubKey(s, Microsoft.Win32.RegistryKeyPermissionCheck.ReadWriteSubTree, System.Security.AccessControl.RegistryRights.FullControl));
}
catch {
}
}
}
regBoot.Close(); //Close.
}
/// <summary>
/// Show Progress bar form.
/// </summary>
void ShowProbar() {
ProgressBarForm proForm = new ProgressBarForm();
_IncHanler = new IncreaseProbarHandler(proForm.IncreaseProbarVal);
proForm.Show();
}
/// <summary>
/// Sub Thread to perform the progress bar.
/// </summary>
void ProThread() {
MethodInvoker mInvoker = new MethodInvoker(ShowProbar);
this.BeginInvoke(mInvoker);
Thread.Sleep(1000);
bool incResult = false; //The status each time when trying to increase the progress bar value.
do {
Thread.Sleep(5);
incResult = (bool)this.Invoke(this._IncHanler, new object[] { 2 });
} while(incResult);
}
Code in the Progress bar form:
/// <summary>
/// Increase the value of the progress bar.
/// </summary>
/// <param name="incVal">The value to increase.</param>
/// <returns>True if increase successful,otherwise false.</returns>
public bool IncreaseProbarVal(int incVal) {
if(incVal <= 0) throw new ArgumentOutOfRangeException("Increase value can't the a negative.");
if(proBar.Value + incVal < proBar.Maximum) {
proBar.Value += incVal;
return true;
}
else {
proBar.Value = proBar.Maximum;
return false;
}
}
Description:
I read the registry key value in the main form recursively using try catch statement.I started a new thread to perform the progress bar form.
The current issue is that,the progress bar form is not appear when running the app.It shows when the main form done(But the value of the progrss bar stay the same,or say not increase).
Some one said that whether I could sure the main jop is not block and has free time to perform the progress bar.I confuse about this and I just don't use the state 'block' or something else.So that must be some other question,or could you launch me someting and have some ideal?
Thanks for your time.
What a mess...a lot of un-necessary threading and Invoke()ing. =\
"The current issue is that,the progress bar form is not appear when running the app.It shows when the main form done"
Your call to RecursiveRegedit() is running on the main thread UI...thus nothing can be updated until the recursive calls are complete.
You need to run RecursiveRegedit() from another thread like you're doing with ProThread().
I have been porting an existing J2ME mobile app, that allows users to view archived news videos, to the latest Nokia SDK 2.0 platform for Series 40 full-touch devices.
I am using both the LWUIT and LWUIT4IO technologies for the UI and Network functionalities of the application respectively.
The app has been tested to work on the S40 5th Edition SDK platform emulator. Extending LWUIT4IO's ConnectionRequest class and utilizing LWUIT's XMLParser, the app can successfully send a HTTP request and get the expected response data from a web service that basically returns an XML-formatted type of feed (containing necessary metadata for the video) (Here's the URL of the web service: http://nokiamusic.myxph.com/nokianewsfeed.aspx?format=3gp)
But for some reason, this is not the case when trying to run the app on the latest Nokia SDK 2.0 platform. It throws a java.lang.NullPointerException upon trying to parse (XMLParser.parse()) the InputStream response of the web service. When I trace the Network Traffic Monitor of the emulator of the corresponding Request sent and Response received - 0 bytes were returned as content despite a successful response status 200. Apparently the XMLParser object has nothing to parse in the first place.
I am hoping that you can somehow shed light on this issue or share any related resolutions, or help me further refine the problem.
Posted below is the code of the SegmentService class (a sub-class of LWUIT's ConnectionRequest) that connects to the webservice and processes the XML response:
public class SegmentService extends ConnectionRequest implements ParserCallback {
private Vector segments;
private Video segment;
public SegmentService(String backend) {
String slash = backend.endsWith("/") ? "" : "/";
setPost(false);
setUrl(backend + slash + "nokianewsfeed.aspx");
addArgument("format", "3gp");
}
public void setDateFilter(String date) {
System.out.println(date);
addArgument("date", date);
}
private Video getCurrent() {
if (segment == null) {
segment = new Video();
}
return segment;
}
protected void readResponse(InputStream input) throws IOException {
InputStreamReader i = new InputStreamReader(input, "UTF-8");
XMLParser xmlparser = new XMLParser();
System.out.println("Parsing the xml...");
Element element = xmlparser.parse(i);
System.out.println("Root " + element.getTagName());
int max = element.getNumChildren();
System.out.println("Number of children: " + max);
segments = new Vector();
for (int c = 0; c < max; c++) {
Element e = element.getChildAt(c);
System.out.println("segment " + c);
int len = e.getNumChildren();
System.out.println("Number of children: " + len);
for (int d=0; d<len; d++) {
Element s = e.getChildAt(d);
String property = s.getTagName();
System.out.println("key: " + property);
String value = (s.getNumChildren()>0) ? s.getChildAt(0).getText() : null;
System.out.println("value: " + value);
if (property.equals("title")) {
getCurrent().setTitle(value);
} else if (property.equals("description")) {
getCurrent().setDescription(value);
} else if (property.equals("videourl")) {
getCurrent().setVideoUrl(value);
} else if (property.equals("thumburl")) {
getCurrent().setThumbUrl(value);
} else if (property.equals("adurl")) {
getCurrent().setAdUrl(value);
} else if (property.equals("publishdate")) {
getCurrent().setPublishDate(value);
} else if (property.equals("category")) {
getCurrent().setCategory(value);
} else if (property.equals("weburl")) {
getCurrent().setWebUrl(value);
} else if (property.equals("thumburl2")) {
getCurrent().setThumb210(value);
} else if (property.equals("thumburl4")) {
getCurrent().setThumb40(value);
}
}
if (segment != null) {
segments.addElement(segment);
segment = null;
}
}
fireResponseListener(new NetworkEvent(this, segments));
}
public boolean parsingError(int errorId, String tag, String attribute, String value, String description) {
System.out.println(errorId);
System.out.println(tag);
System.out.println(value);
System.out.println(description);
return true;
}
}
I try to get the high qulity antialiasing from a tuturial I found on the internet (http://www.rkoenig.eu/index.php?option=com_content&view=article&id=21:chapter-3-das-erste-echte-3d-objekt&catid=6:directx10-basics&Itemid=3). But did not achieve a very good solution.
I already set the multisampling to the maximum:
m_swapChainDesc.SampleDescription = new DXGI.SampleDescription(8,0);
To me it appears as the pixel size of the rendered image is larger than the actual pixel size of my screen.
Thank you very much in advance for your valuable inputs
here is the complete code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using SlimDX;
using DX10 = SlimDX.Direct3D10;
using DXGI = SlimDX.DXGI;
namespace TutorialSeries.DirectX10.Chapter3
{
public partial class MainWindow : Form
{
private DX10.Device m_device;
private DXGI.SwapChainDescription m_swapChainDesc;
private DXGI.SwapChain m_swapChain;
private DXGI.Factory m_factory;
private DX10.RenderTargetView m_renderTarget;
private bool m_initialized;
private SimpleBox m_simpleBox;
private Matrix m_viewMatrix;
private Matrix m_projMatrix;
private Matrix m_worldMatrix;
private Matrix m_viewProjMatrix;
public MainWindow()
{
InitializeComponent();
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.Opaque, true);
}
/// <summary>
/// Initializes device and other resources needed for rendering. Returns true, if successful.
/// </summary>
private bool Initialize3D()
{
try
{
m_device = new DX10.Device(DX10.DriverType.Warp, DX10.DeviceCreationFlags.SingleThreaded);
m_factory = new DXGI.Factory();
m_swapChainDesc = new DXGI.SwapChainDescription();
m_swapChainDesc.OutputHandle = this.Handle;
m_swapChainDesc.IsWindowed = true;
m_swapChainDesc.BufferCount = 1;
m_swapChainDesc.Flags = DXGI.SwapChainFlags.AllowModeSwitch;
m_swapChainDesc.ModeDescription = new DXGI.ModeDescription(
this.Width,
this.Height,
new Rational(60, 1),
DXGI.Format.R8G8B8A8_UNorm);
m_swapChainDesc.SampleDescription = new DXGI.SampleDescription(8,0);
m_swapChainDesc.SwapEffect = DXGI.SwapEffect.Discard;
m_swapChainDesc.Usage = DXGI.Usage.RenderTargetOutput;
m_swapChain = new DXGI.SwapChain(m_factory, m_device, m_swapChainDesc);
DX10.Viewport viewPort = new DX10.Viewport();
viewPort.X = 0;
viewPort.Y = 0;
viewPort.Width = this.Width;
viewPort.Height = this.Height;
viewPort.MinZ = 0f;
viewPort.MaxZ = 1f;
//DX10.Texture2D backBuffer = m_swapChain.GetBuffer<DX10.Texture2D>(0);
DX10.Texture2D Texture = DX10.Texture2D.FromSwapChain<DX10.Texture2D>(m_swapChain,0);
//m_renderTarget = new DX10.RenderTargetView(m_device, backBuffer);
//DX10.RenderTargetViewDescription renderDesc = new DX10.RenderTargetViewDescription();
//renderDesc.FirstArraySlice = 0;
//renderDesc.MipSlice = 0;
m_renderTarget = new DX10.RenderTargetView(m_device, Texture);
Texture.Dispose();
DX10.RasterizerStateDescription rsd = new DX10.RasterizerStateDescription();
rsd.CullMode = DX10.CullMode.Back;
rsd.FillMode = DX10.FillMode.Wireframe;
rsd.IsMultisampleEnabled = true;
rsd.IsAntialiasedLineEnabled = false;
rsd.IsDepthClipEnabled = false;
rsd.IsScissorEnabled = false;
DX10.RasterizerState RasterStateWireFrame = DX10.RasterizerState.FromDescription(m_device,rsd);
DX10.BlendStateDescription blendDesc = new DX10.BlendStateDescription();
blendDesc.BlendOperation = DX10.BlendOperation.Add;
blendDesc.AlphaBlendOperation = DX10.BlendOperation.Add;
blendDesc.SourceAlphaBlend = DX10.BlendOption.Zero;
blendDesc.DestinationAlphaBlend = DX10.BlendOption.Zero;
blendDesc.SourceBlend = DX10.BlendOption.SourceColor;
blendDesc.DestinationBlend = DX10.BlendOption.Zero;
blendDesc.IsAlphaToCoverageEnabled = false;
blendDesc.SetWriteMask(0, DX10.ColorWriteMaskFlags.All);
blendDesc.SetBlendEnable(0, true);
DX10.BlendState m_blendState = DX10.BlendState.FromDescription(m_device, blendDesc);
m_device.Rasterizer.State = RasterStateWireFrame;
m_device.Rasterizer.SetViewports(viewPort);
m_device.OutputMerger.BlendState = m_blendState;
m_device.OutputMerger.SetTargets(m_renderTarget);
m_viewMatrix = Matrix.LookAtLH(
new Vector3(0f, 0f, -4f),
new Vector3(0f, 0f, 1f),
new Vector3(0f, 1f, 0f));
m_projMatrix = Matrix.PerspectiveFovLH(
(float)Math.PI * 0.5f,
this.Width / (float)this.Height,
0.1f, 100f);
m_viewProjMatrix = m_viewMatrix * m_projMatrix;
m_worldMatrix = Matrix.RotationYawPitchRoll(0.85f, 0.85f, 0f);
m_simpleBox = new SimpleBox();
m_simpleBox.LoadResources(m_device);
m_initialized = true;
}
catch (Exception ex)
{
MessageBox.Show("Error while initializing Direct3D10: \n" + ex.Message);
m_initialized = false;
}
return m_initialized;
}
/// <summary>
/// Rendering is done during the standard OnPaint event
/// </summary>
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (m_initialized)
{
m_device.ClearRenderTargetView(m_renderTarget, new Color4(Color.CornflowerBlue));
m_simpleBox.Render(m_device, m_worldMatrix, m_viewProjMatrix);
m_swapChain.Present(0, DXGI.PresentFlags.None);
}
}
/// <summary>
/// Initialize 3D-Graphics within OnLoad event
/// </summary>
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Initialize3D();
}
}
}
This is an old question but it's a shame it never got answered; I stumbled across it on Google so I figure answering it may help someone else in the future...
First of all, Pascal, you did NOT set MSAA to the maximum... You're using 8:0, which means 8 samples at a quality of 0 (zero)... definitely not the maximum. What the "max" is depends on the GPU installed on the local machine. So it varies from PC to PC. That's why a DirectX application needs to use DXGI to properly enumerate hardware devices and determine what settings are valid. This is no trivial topic and will require you to do some research and practice of your own. The DirectX SDK Documentation and samples/tutorials are a great starting place, and there's a lot of other materials to be found online. But on my machine, for instance, my GTX-580 GPU can support 8:16 MSAA (possibly higher, but haven't checked).
So you need to learn to use DXGI to enumerate your graphics cards and monitors and figure out what MSAA levels (and other graphics features/settings) it can support. That's the only way you'll ever figure out the "max" MSAA settings or the correct refresh rate of your monitor, for example. If you're clever you will write yourself a small library or component for your game engine that will enumerate hardware devices for you and figure out the optimal graphics settings so you won't have to re-do this over and over for future projects.
Regards,
--ATC--